עדכונים אוטומטיים של פיד מוצרים לפי לוח זמנים ב-Laravel

עדכונים אוטומטיים של פיד מוצרים לפי לוח זמנים ב-Laravel

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
עדכונים אוטומטיים של פיד מוצרים לפי לוח זמנים ב-Laravel
פשוט
מ- 1 יום עד 3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1467
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1320
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1015
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1276
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1019
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1019

עדכון אוטומטי של פיד מוצרים לפי לוח זמנים ב-Laravel

השקתם חנות מסחר אלקטרוני ב-Laravel, שילבתם את Yandex.Market ו-Google Merchant, אבל אחרי שבוע שמתם לב: המחירים בפיד מיושנים, מוצרים שאזלו מהמלאי עדיין מופיעים במרקטפלייס, והפלטפורמה שולחת אזהרות. ייצוא ידני כל שעה אינו אופציה; קובץ CSV סטטי פעם ביום לא יכול לעמוד בקצב הדינמיקה של המלאי. עדכונים אוטומטיים לפי לוח זמנים הם הדרך היחידה לשמור על פידים עדכניים. אנו מיישמים זאת באמצעות מתזמן המובנה של Laravel וארכיטקטורת גנרטור גמישה.

ייצור פיד אוטומטי מהיר פי 100 מייצוא ידני עבור קטלוג של 10,000 מוצרים. שגיאות במהלך ייצוא ידני מתרחשות ב-3% מהמקרים; עם אוטומציה, זה 0.01% – שיפור של 99.7%. חיסכון חודשי בזמן עולה על 20 שעות – שווה ערך לכ-$200 משכורת מנהל. מניעת חסימות מרקטפלייס שומרת על תקציב פרסום ממוצע של $500 בחודש.

מדוע עדכוני פיד אוטומטיים קריטיים למכירות?

קונה רואה מוצר מיושן ועוזב למתחרה. Yandex.Market חוסם חנויות עם פידים ישנים מ-24 שעות. Google Merchant משביתה מוצרים עם מחירים שגויים. אוטומציה מבטלת סיכונים אלה: פידים נוצרים בתדירות הנדרשת, שגיאות מתועדות, וטריות מנוטרת על ידי ניטור נפרד. השוו: ייצוא ידני לוקח 30 דקות עבור 10,000 מוצרים; ייצור אוטומטי לוקח פחות מדקה. לפי תיעוד Laravel, המתזמן יכול להריץ משימות בדיוק של דקה.

פורמטי פיד: טבלת השוואה

פלטפורמה פורמט סיומת מאפיינים
Yandex.Market YML (Yandex Market Language) .xml תג available נדרש, תומך ב-market_category
Google Merchant RSS 2.0 + מרחב שמות g: .xml, .tsv דורש g:price, g:availability, g:mpn
Facebook/Instagram CSV או XML .csv, .xml שדות: id, title, description, image_link, price
Avito XML מותאם אישית .xml פורמט ייחודי עם AdId, Category, Price

אותו קטלוג חייב להיות מיוצא במספר פורמטים. הארכיטקטורה חייבת לקחת זאת בחשבון מההתחלה.

ארכיטקטורת גנרטור הפיד

אנו מתכננים את המערכת על בסיס FeedGeneratorInterface ו-FeedGeneratorFactory. כל גנרטור מיישם את מתודת generate(FeedConfig $config) המחזירה תוכן פיד. החלטות מפתח:

  • שאילתות Cursor – אין לטעון את כל המוצרים לזיכרון, לטפל באלפי פריטים ללא חריגה ממגבלות.
  • שינוי שם אטומי – כתיבה לקובץ זמני, ולאחר מכן rename(): אם התהליך מופרע, האגרגטור מקבל את הגרסה המלאה הישנה, לא קובץ קטוע.
  • הממשק מאפשר הוספת פורמט חדש ללא שינוי קוד קיים.
// app/Services/Feed/FeedGenerator.php interface FeedGeneratorInterface { public function generate(FeedConfig $config): string; public function format(): string; // 'yml', 'csv', 'xml' } class YandexMarketFeedGenerator implements FeedGeneratorInterface { public function format(): string { return 'yml'; } public function generate(FeedConfig $config): string { $products = Product::query() ->where('is_active', true) ->whereHas('stock', fn($q) => $q->where('quantity', '>', 0)) ->when($config->category_ids, fn($q, $ids) => $q->whereIn('category_id', $ids)) ->with(['category', 'images', 'attributes']) ->cursor(); $xml = new \XMLWriter(); $xml->openMemory(); $xml->setIndent(true); $xml->startDocument('1.0', 'UTF-8'); $xml->startElement('yml_catalog'); $xml->writeAttribute('date', now()->format('Y-m-d H:i')); $xml->startElement('shop'); $this->writeShopInfo($xml, $config); $xml->startElement('offers'); foreach ($products as $product) { $this->writeOffer($xml, $product, $config); } $xml->endElement(); $xml->endElement(); $xml->endElement(); return $xml->outputMemory(); } private function writeOffer(\XMLWriter $xml, Product $product, FeedConfig $config): void { $xml->startElement('offer'); $xml->writeAttribute('id', $product->id); $xml->writeAttribute('available', $product->stock->quantity > 0 ? 'true' : 'false'); $xml->writeElement('url', route('product.show', $product->slug)); $xml->writeElement('price', number_format($product->price, 2, '.', '')); $xml->writeElement('currencyId', $config->currency ?? 'USD'); $xml->writeElement('categoryId', $product->category_id); $xml->writeElement('name', $product->name); $xml->writeElement('description', strip_tags($product->description)); foreach ($product->images->take(10) as $image) { $xml->writeElement('picture', $image->url); } $xml->endElement(); } } 

כיצד להגדיר לוחות זמנים לסוגי פיד שונים?

אנו משתמשים במתזמן של Laravel עם ביטויי cron דינמיים ממסד הנתונים. זה מאפשר שינוי לוחות זמנים ללא פריסה. תדירויות אופייניות:

סוג נתונים תדירות דוגמת cron
מחירים ומלאי כל 15 דקות // app/Services/Feed/FeedGenerator.php interface FeedGeneratorInterface { public function generate(FeedConfig $config): string; public function format(): string; // 'yml', 'csv', 'xml' } class YandexMarketFeedGenerator implements FeedGeneratorInterface { public function format(): string { return 'yml'; } public function generate(FeedConfig $config): string { $products = Product::query() ->where('is_active', true) ->whereHas('stock', fn($q) => $q->where('quantity', '>', 0)) ->when($config->category_ids, fn($q, $ids) => $q->whereIn('category_id', $ids)) ->with(['category', 'images', 'attributes']) ->cursor(); $xml = new \XMLWriter(); $xml->openMemory(); $xml->setIndent(true); $xml->startDocument('1.0', 'UTF-8'); $xml->startElement('yml_catalog'); $xml->writeAttribute('date', now()->format('Y-m-d H:i')); $xml->startElement('shop'); $this->writeShopInfo($xml, $config); $xml->startElement('offers'); foreach ($products as $product) { $this->writeOffer($xml, $product, $config); } $xml->endElement(); $xml->endElement(); $xml->endElement(); return $xml->outputMemory(); } private function writeOffer(\XMLWriter $xml, Product $product, FeedConfig $config): void { $xml->startElement('offer'); $xml->writeAttribute('id', $product->id); $xml->writeAttribute('available', $product->stock->quantity > 0 ? 'true' : 'false'); $xml->writeElement('url', route('product.show', $product->slug)); $xml->writeElement('price', number_format($product->price, 2, '.', '')); $xml->writeElement('currencyId', $config->currency ?? 'USD'); $xml->writeElement('categoryId', $product->category_id); $xml->writeElement('name', $product->name); $xml->writeElement('description', strip_tags($product->description)); foreach ($product->images->take(10) as $image) { $xml->writeElement('picture', $image->url); } $xml->endElement(); } }
תיאורים ומאפיינים כל שעה // app/Console/Commands/GenerateFeed.php class GenerateFeed extends Command { protected $signature = 'feed:generate {feed_id?} {--all}'; protected $description = 'Generate product feed files'; public function handle(): int { $configs = $this->option('all') ? FeedConfig::where('is_active', true)->get() : FeedConfig::whereKey($this->argument('feed_id'))->get(); foreach ($configs as $config) { $this->generateOne($config); } return self::SUCCESS; } private function generateOne(FeedConfig $config): void { $start = microtime(true); try { $generator = FeedGeneratorFactory::make($config->type); $content = $generator->generate($config); $tmp = $config->output_path . '.tmp'; file_put_contents(public_path($tmp), $content); rename(public_path($tmp), public_path($config->output_path)); $config->update([ 'last_run_at' => now(), 'last_error' => null, ]); $this->info(sprintf( '[%s] %s generated in %.2fs (%s)', $config->name, basename($config->output_path), microtime(true) - $start, $this->formatBytes(strlen($content)) )); } catch (\Throwable $e) { $config->update(['last_error' => $e->getMessage()]); $this->error("[{$config->name}] Failed: " . $e->getMessage()); report($e); } } }
ייצוא מלא עם תמונות פעם ביום בלילה */15 * * * *
// app/Console/Commands/GenerateFeed.php class GenerateFeed extends Command { protected $signature = 'feed:generate {feed_id?} {--all}'; protected $description = 'Generate product feed files'; public function handle(): int { $configs = $this->option('all') ? FeedConfig::where('is_active', true)->get() : FeedConfig::whereKey($this->argument('feed_id'))->get(); foreach ($configs as $config) { $this->generateOne($config); } return self::SUCCESS; } private function generateOne(FeedConfig $config): void { $start = microtime(true); try { $generator = FeedGeneratorFactory::make($config->type); $content = $generator->generate($config); $tmp = $config->output_path . '.tmp'; file_put_contents(public_path($tmp), $content); rename(public_path($tmp), public_path($config->output_path)); $config->update([ 'last_run_at' => now(), 'last_error' => null, ]); $this->info(sprintf( '[%s] %s generated in %.2fs (%s)', $config->name, basename($config->output_path), microtime(true) - $start, $this->formatBytes(strlen($content)) )); } catch (\Throwable $e) { $config->update(['last_error' => $e->getMessage()]); $this->error("[{$config->name}] Failed: " . $e->getMessage()); report($e); } } } 

בשרת, יש צורך רק ברישום crontab אחד:

// app/Console/Kernel.php protected function schedule(Schedule $schedule): void { FeedConfig::where('is_active', true)->each(function (FeedConfig $config) use ($schedule) { $schedule->command("feed:generate {$config->id}") ->cron($config->schedule) ->withoutOverlapping(10) ->runInBackground() ->onFailure(function () use ($config) { Notification::route('slack', config('services.slack.webhook')) ->notify(new FeedGenerationFailed($config)); }); }); } 

ניטור טריות וגישה ציבורית

פידים מאוחסנים ב-0 * * * *. להגנה, השתמשו באימות Basic של HTTP או בכתובות URL חתומות:

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1 

פקודה נוספת בודקת אם פיד מיושן (מעל 60 דקות) ושולחת התראה לניטור. זה מונע חסימות פלטפורמה.

כיצד להימנע מחסימות מרקטפלייס?

השתמשו בשינוי שם אטומי: הפיד המוצג לפלטפורמה אף פעם לא שבור. אם הייצור מופרע עקב timeout במסד הנתונים או כשל רשת, הגרסה המלאה הקודמת נשארת על הדיסק. בנוסף, נשלחות התראות ל-Telegram כאשר זמן הייצור עולה על פי שניים מהממוצע.

רשימת בדיקה של טעויות נפוצות בהגדרת פיד:

  • שאילתת N+1 בבחירת מוצרים – השתמשו ב-eager loading או בשאילתות cursor.
  • תווים XML לא חוקיים בתיאורים – בריחה באמצעות 0 3 * * *.
  • תג // app/Console/Kernel.php protected function schedule(Schedule $schedule): void { FeedConfig::where('is_active', true)->each(function (FeedConfig $config) use ($schedule) { $schedule->command("feed:generate {$config->id}") ->cron($config->schedule) ->withoutOverlapping(10) ->runInBackground() ->onFailure(function () use ($config) { Notification::route('slack', config('services.slack.webhook')) ->notify(new FeedGenerationFailed($config)); }); }); } חסר עבור YML – המוצר לא יופיע בתוצאות.
  • גודל פיד גדול מדי (>100 MB) – חלקו לחלקים או השתמשו ב-gzip.

מה כלול בעבודה

  1. ניתוח – סקירת הקטלוג שלכם, דרישות המרקטפלייס, תשתית קיימת.
  2. עיצוב – ארכיטקטורת גנרטור, תצורה, סכמת DB, לוח זמנים.
  3. יישום – כתיבת גנרטורים לפורמטים הנדרשים, אינטגרציה עם המתזמן, הגדרת ניטור.
  4. בדיקות – אימות עם פיד בדיקה, אימות בפלטפורמות, בדיקות עומס.
  5. פריסה ותיעוד – פריסה על השרת שלכם, הוראות ניהול מלוח הבקרה.

מעל 7+ שנות ניסיון בפיתוח Laravel, 50+ אינטגרציות מוצלחות עם מרקטפלייס. אנו מבטיחים פעילות מערכת יציבה ותמיכה מהירה לאחר הפריסה. צרו קשר לייעוץ חינם – ננתח את הקטלוג שלכם ונציע לוח זמנים אופטימלי. הזמינו יישום מערכת וקבלו אינטגרציה מוכנה עם התראות מוגדרות תוך 3-4 ימים.