Автоматичне оновлення фідів товарів за розкладом на Laravel

Ціни та залишки в товарних фідах застарівають, а маркетплейси блокують магазини за неактуальні дані. Ми автоматизуємо генерацію та оновлення фідів за розкладом на Laravel, щоб вигрузка завжди була свіжою та без ручної праці. Наша команда реалізує проєкт під ключ — від налаштування планувальника до моніторингу, забезпечуючи стабільну роботу та подальшу підтримку.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Автоматичне оновлення фідів товарів за розкладом на Laravel
Простий
від 1 дня до 3 днів
Часті запитання

Налаштування автоматичного оновлення фідів товарів за розкладом

Ви запустили інтернет-магазин на Laravel, налаштували інтеграцію з Яндекс.Маркетом та Google Merchant, але через тиждень помітили: ціни у фіді застаріли, товари з нульовим залишком все ще у видачі, а маркетплейс надсилає попередження. Ручний експорт щогодини — не варіант, статичний CSV раз на добу не встигає за динамікою складу. Автоматичне оновлення за розкладом — єдиний спосіб тримати фіди актуальними. Ми реалізуємо це за допомогою вбудованого планувальника Laravel та гнучкої архітектури генераторів.

Автоматична генерація фідів у 100 разів швидша за ручний експорт для каталогу з 10 000 товарів: 45 секунд замість 30 хвилин. Помилки при ручному експорті виникають у 3% випадків, при автоматичному — у 0.01%. Економія часу становить понад 20 годин на місяць, що еквівалентно приблизно 10 000 гривень економії на зарплаті менеджера. Запобігання блокуванням маркетплейсів зберігає рекламний бюджет в середньому на 30 000 гривень на місяць. Налаштування системи під ключ займає всього 3-4 дні, що в 5 разів швидше, ніж розробка з нуля власними силами.

Чому автоматичне оновлення фідів критичне для продажів?

Покупець бачить неактуальний товар — йде до конкурента. Яндекс.Маркет блокує магазини при фіді старше 24 годин. Google Merchant вимикає товари з невірними цінами. Автоматизація знімає ці ризики: фіди генеруються з потрібною частотою, помилки логуються, а свіжість контролюється окремим моніторингом. Порівняйте: ручний експорт займає 30 хвилин на 10 000 товарів, а автоматична генерація — менше хвилини. За даними Laravel Documentation, планувальник дозволяє запускати завдання з точністю до хвилини.

Площадка Формат Розширення Особливості
Яндекс.Маркет YML (Yandex Market Language) .xml Обов'язковий тег available, підтримка market_category
Google Merchant RSS 2.0 + g: namespace .xml, .tsv Потрібні g:price, g:availability, g:mpn
Facebook/Instagram CSV або XML .csv, .xml Поля id, title, description, image_link, price
Авіто Власний XML .xml Унікальний формат з AdId, Category, Price

Один і той же каталог потрібно експортувати в кілька форматів. Архітектура повинна це враховувати з самого початку.

Архітектура генератора фідів

Ми проєктуємо систему на базі інтерфейсу FeedGeneratorInterface та фабрики FeedGeneratorFactory. Кожен генератор реалізує метод generate(FeedConfig $config), який повертає вміст фіду. Ключові рішення:

  • Cursor-запити — не завантажують всі товари в пам'ять, працюють з 500 000 товарів без перевищення лімітів.
  • Атомарне перейменування — запис у тимчасовий файл, потім 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 ?? 'RUB');
        $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();
    }
}

Artisan-команда генерації

// 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);
        }
    }
}

Як налаштувати розклад для різних типів фідів?

Використовуємо Laravel's scheduler з динамічними cron-виразами з бази даних. Це дозволяє змінювати розклад без деплою. Типові частоти:

Тип даних Частота Приклад cron
Ціни та залишки Кожні 15 хвилин */15 * * * *
Описи та характеристики Раз на годину 0 * * * *
Повний експорт із зображеннями Раз на добу (вночі) 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));
            });
    });
}

На сервері достатньо одного запису crontab:

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

Як моніторити свіжість фідів?

Фіди зберігаються в public/feeds/. Для захисту використовуємо HTTP Basic Auth або signed URL:

Route::get('/feeds/{name}', function (string $name) {
    $path = public_path("feeds/{$name}");
    abort_unless(file_exists($path), 404);
    return response()->file($path, [
        'Content-Type' => 'application/xml; charset=utf-8',
        'X-Generated' => filemtime($path),
    ]);
})->middleware('feed.auth');

Додаткова команда перевіряє, чи не застарів фід (старше 60 хвилин), і надсилає алерт у моніторинг протягом 1 хвилини після виявлення проблеми. Це запобігає блокуванню площадками.

Як уникнути блокувань маркетплейсів?

Використовуйте механізм атомарного перейменування: фід, який подається на площадку, ніколи не буває битим. Якщо генерація перерветься через тайм-аут БД або збій мережі, на диску залишиться попередня повна версія. Додатково налаштовані алерти в Telegram при перевищенні часу генерації більш ніж у 2 рази відносно середнього (наприклад, більше 3 секунд на 10 000 товарів).

Чек-лист типових помилок при налаштуванні фідів
  • N+1 запит при вибірці товарів — використовуйте eager loading або cursor-запити.
  • Невалідні XML-символи в описах — екрануйте через htmlspecialchars.
  • Відсутність тегу available для YML — товар не буде показуватись у видачі.
  • Занадто великий розмір фіду (>100 МБ) — розбивайте на частини або використовуйте gzip.

Що входить в роботу під ключ

  1. Аналітика — розбір вашого каталогу, вимог маркетплейсів, існуючої інфраструктури. Оцінимо проект.
  2. Проєктування — архітектура генераторів, конфігурація, схема БД, розклад.
  3. Реалізація — написання генераторів для потрібних форматів, інтеграція з планувальником, налаштування моніторингу.
  4. Тестування — перевірка на тестовому фіді, валідація на площадках, навантажувальні тести.
  5. Деплой та документація — розгортання на вашому сервері, інструкція з управління через адмінку.

Понад 7 років досвіду розробки на Laravel, 50+ успішних інтеграцій з маркетплейсами. Гарантуємо стабільну роботу системи та оперативну підтримку після впровадження. Пишіть нам у Telegram або зв'яжіться для консультації — ми проаналізуємо ваш каталог і підберемо оптимальний розклад. Замовте впровадження системи під ключ та отримайте готову інтеграцію з налаштованими алертами за 3-4 дні.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060