[СДЕЛАЙ САМ] Импорт товаров MiniShop2 из xml фида Яндекс.Маркета

Приветствую! За последний месяц второй раз прилетает задача сделать импорт товаров из фида Яндекс.Маркета. Может быть я не один такой, поэтому делюсь своим решением.

Возможности
1. Импорт дерева категорий (ВАЖНО! категории в файле должны быть расположены сверху вниз в порядке увеличения уровня вложенности);
2. Импорт товаров (*есть возможность указать соответствие полей товара в файле и на сайте в свойстве конфигурации «productFields»);
3. Импорт опций товаров из тэгов «param» (опции создаются автоматически);
4. Импорт галереи товаров из тэгов «picture»;
5. Импорт производителей (*есть возможность указать соответствие полей производителя в файле и на сайте в свойстве конфигурации «vendorFields»);

*если нужно передать значение для которого не предусмотрен отдельный тэг, то следует использовать тэг param, при этом в качестве соответствия нужно указывать содержимое атрибута «name» с префиксом «name_». Например вы хотите передать логотип производителя
<param name="ЛогоВендора">https://art-sites.ru//assets/components/phpthumbof/cache/logo-icon-w.4113b3f984c288dac851d68881c111ed.webp</param>
в этом случае в свойстве «vendorFields» должна быть такая запись
'vendorFields'  => [
...
'logo' => 'name_ЛогоВендора',
...
]


Импорт других сущностей не поддерживается, но при желании можете дописать.

Быстрый старт
1. Скачать из этого репозитория файлы «importfeed.class.php» и «importconfig.inc.php».
2. Выставить необходимые настройки в файле «importconfig.inc.php».
3. Установить компонент ExtraFields и создать с его помощью поле «feed_id».
4. Запустить через терминал или по CRON файл «importfeed.class.php», используя php>= 7.4.

Особенности
1. Скачанные файлы должны находится в папке «core/elements» иначе нужно менять путь к корневому файлу «index.php»;
2. В конфигурации установлен шаг в 100 ресурсов, его можно изменить, но это способно привести к ошибке с нехваткой памяти при большом количестве товаров. Рекомендую повесить запуск на крон и ставить ша не более 500.
3. Пока работает скрипт не удаляйте файл readingdata.json в нём хранится информация о последней завершённой итерации. Данный файл будет автоматически удалён после завершения всех импортов.
4. После завершения всех импортов будет создан файл importfinished.txt, который не позволит запустить импорт повторно в автоматическом режиме. Соответственно, если нужно запустить импорт снова, удалите этот файл.
5. Ресурсы создаются без использования процессоров, поэтому никакие плагины на события типа OnDocFormSave и других не сработают. Так сделано для ускорения процесса и потому что задача не обязывала использовать процессоры.
6. Все фото добавленные в галерею будут удалены из исходной папки, потому что дубли никому не нужны.

Возможные ошибки.
1. Дубли в названиях могут привести к ошибке превышения объёма памяти или «Segmentation fault» (чтобы это ни значило). Есть два варианта решения, либо вручную исправить проблемные названия, либо в файле конфигурации установить свойству «createUniquePagetitle» значение true.
2. Проблемы с загрузкой картинок в формате WEBP. Галерея минишопа выдаёт ошибку «Не верный формат файла». Добавление формата в разрешённые в источнике файлов проблему не решило.

Отслеживание ошибок.
В скрипт встроено подробное логирование, информация о ходе выполнения пишется в файл «import_log.txt». так же скрипт можно выполнять поэтапно задав начальную и конечную позицию в файле «readingdata.json».

Поблагодарить автора, а также немного порадовать других активных участников сообщества можно, отправив донат одним из следующих способов
Артур Шевченко
17 апреля 2023, 13:53
modx.pro
2
1 989
+11
Поблагодарить автора Отправить деньги

Комментарии: 43

Сергей С
16 июня 2023, 13:01
0
Будет ли работать если фид на стороннем сайте? Умеет ли обновлять уже загруженные товары? Подтянет ли фото со стороннего сайта? Зачем нужен feed_id?
    Артур Шевченко
    17 июня 2023, 12:09
    0
    Будет ли работать если фид на стороннем сайте?
    Будет, фид сначала скачивается на ваш сервер.

    Умеет ли обновлять уже загруженные товары?
    Если название в фиде и pagetitle на сайте совпадают, то да.

    Подтянет ли фото со стороннего сайта?
    Фото тянутся исключительно по полной ссылке, даже если они на вашем сервере лежат, они должны быть доступны по ссылке вида domain.ru/imagename.jpg

    Зачем нужен feed_id?
    По нему определяется вложенность категорий и принадлежность товара к категории
    Сергей С
    16 июня 2023, 13:17
    0
    В какую настройку прописывать поле изображений? Несколько изображений он загрузит? в ms2gallery?
      Артур Шевченко
      17 июня 2023, 12:10
      0
      Пjле изображений это всегда picture.
        Сергей С
        17 июня 2023, 12:34
        0
        так?
        'productFields' => [
        'weight' => 'name_Вес',
        'pagetitle' => 'name',
        'content' => 'description',
        'made_in' => 'country_of_origin',
        'price' => 'price',
        'old_price' => 'oldprice',
        'picture' => 'picture',
        ],
          Артур Шевченко
          17 июня 2023, 15:33
          0
          А что это? Поля товара? Если да, то нет указывать там picture не надо
    Сергей С
    12 июля 2023, 12:12
    0
    все скачал и настроил но не работает и выдает ошибку синтаксиса
    PHP Parse error: syntax error, unexpected 'ModX' (T_STRING), expecting function (T_FUNCTION) or const (T_CONST)
    по этим строкам public ModX $modx;
    public string $basepath;
    public string $logpath;
    public string $feedPath;
    public string $imagePath;
    public string $finishFilePath;
    public string $readingDataPath;
    public array $config;
    public array $options;
    PHP 7.4 modx 2.8.5
      Артур Шевченко
      12 июля 2023, 20:13
      0
      Точно запускаешь с php 7.4?
        Сергей С
        12 июля 2023, 20:22
        0
        Ага.
        PHP модуль Apache 7.4.29 (alt)
        Сразу полез проверять т.к. подумал что просто не поддерживается версией php
          Сергей С
          12 июля 2023, 20:25
          0
            Артур Шевченко
            12 июля 2023, 20:46
            0
            Покажи команду которой запускаешь скрипт?
              Сергей С
              12 июля 2023, 20:49
              0
              /opt/php72/bin/php /var/www/u1145459/data/www/glav-site.ru/core/elements/importfeed.class.php > /dev/null 2>&1
              или
              /opt/php72/bin/php /var/www/u1145459/data/www/glav-site.ru/core/elements/importfeed.class.php
              по крону.
              и
              php /var/www/u1145459/data/www/glav-site.ru/core/elements/importfeed.class.php
              /opt/php72/bin/php /var/www/u1145459/data/www/glav-site.ru/core/elements/importfeed.class.php
              из ssh
                Артур Шевченко
                12 июля 2023, 20:56
                0
                Ну дык ты запускаешь с версией 7.2
                  Сергей С
                  12 июля 2023, 20:59
                  0
                  вот же. И там все команды такие. ща попробую перезапустить под 74. Спасибо
                    Сергей С
                    12 июля 2023, 21:28
                    0
                    Поменял версию. Тишина. и в журнале. Срипт куда логи пишет? Скрипт точно подтягивает настройки из importconfig.inc.php, но дальше где-то падает.
                      Артур Шевченко
                      12 июля 2023, 21:32
                      0
                      Срипт куда логи пишет?
                      Сюда
                        Сергей С
                        12 июля 2023, 21:45
                        0
                        запускается файл создает, лог пишет, но не может скачать файл. В чем может быть причина? файл открывается в браузере нормально. В настройках importconfig.inc.php где можно накосячить чтобы не скачивался?
                        Сергей С
                        12 июля 2023, 21:32
                        0
                        Нашел
            Сергей С
            13 июля 2023, 18:05
            0
            Запустить удалось. Пишет в лог следующее:
            13.07.2023 18:06:49 [ImportFeed::start] Начат импорт товаров.
            13.07.2023 18:06:49 [ImportFeed::startReading] Импортируем ресурс с порядковым номером 1
            13.07.2023 18:06:49 [ImportFeed::startReading] memory usage: 8,9995651245117
            13.07.2023 18:06:49 [ImportFeed::createResource] Будет обработан ресурс со следующими данными.Array
            (
                [feed_id] => 36120
            )
            
            13.07.2023 18:06:49 [ImportFeed::createResource] Будет обработан ресурс со следующими данными.Array
            (
                [feed_id] => 36120
                [pagetitle] => Ресурс 1689260809
                [alias] => resurs-1689260809
            )
            Импорт не происходит в итоге. 
            сам xml максимально упростил 
            <?xml version="1.0" encoding="UTF-8"?>
            <shop>
            <categories>
            <category id="36120">Спальня</category>
            </categories>
            <offers>
            <offer id="89" group_id="89" available="false">
            <price>7600.00</price>
            <categoryId>36120</categoryId>
            <name>Прикроватная тумба Лофт Панара</name>
            </offer>
            </offers>
            </shop>
            В какую сторону копать?
              Артур Шевченко
              13 июля 2023, 21:17
              0
              Во-первых проверить настройки. Во-вторых, поле feed_id у ресурсов есть?
                Сергей С
                16 июля 2023, 14:00
                0
                Да. Все это было. проблема оказалась в категориях.Их не было и импорт тормозил на этом этапе. Включил создание категорий. Теперь все работает, спасибо!
              Сергей С
              16 июля 2023, 14:13
              0
              А есть Вариант привязать изображения к ms2Gallery. Сейчас грузит в стандартную галерею минишопа.
                Артур Шевченко
                16 июля 2023, 14:15
                0
                Конечно есть, надо переписать метод который грузит картинки так, чтобы он грузил в ms2Gallery
                  Сергей С
                  16 июля 2023, 14:25
                  0
                  спасибо. Нашел переписал
                Дмитрий
                25 июля 2023, 15:16
                0
                Добрый день! Обычный XML не подойдет? Только YML?
                  Артур Шевченко
                  25 июля 2023, 16:04
                  0
                  Нет, там же вполне определенные тэги, только переписывать.
                  Сергей С
                  25 июля 2023, 20:06
                  0
                  Нашел причину почему не грузит с удаленного сервера фид. importfeed.class.php 76 строка опечатка feelUrl
                    Сергей С
                    24 августа 2023, 17:56
                    0
                    Добрый день! Возник вопрос, а как Выцепить id из Фида? . Вот этот 999 имеется ввиду
                      Артур Шевченко
                      24 августа 2023, 22:32
                      0
                      Если речь про id оффера, то для него нужно создать поле feed_id
                        Сергей С
                        24 августа 2023, 22:52
                        0
                        Это уже создано. Он при импорте записывается. Тут вопрос немного другой получактая. Там сейчас идет проверка существует ли объект на сайте по pagetitle. А нужно по id. Я так понимаю нужно переписать строку про где newObject, проверив по if (! $id = товар с feed_id)?
                          Артур Шевченко
                          24 августа 2023, 22:58
                          0
                          Вот тут поле поменять надо
                            Сергей С
                            24 августа 2023, 23:01
                            0
                            Ага. Я про неё и имел ввиду. Заменяем pagetitle на feed_id, а ресурс на $id(предварительно получив его $id = $item->attributes()->id)?
                              Артур Шевченко
                              24 августа 2023, 23:18
                              0
                              Не понимаю о каком ресурсе речь
                                Сергей С
                                24 августа 2023, 23:21
                                0
                                В этой строке resource.
                                  Артур Шевченко
                                  24 августа 2023, 23:24
                                  0
                                  А почему ты хочешь положить объект класса modResource в переменную с именем id?
                                    Сергей С
                                    25 августа 2023, 08:54
                                    0
                                    Видать я неправильно прочитал код. Тогда возвращаюсь к вопросу как получить id оффера для поиска товара с таким feed_id на сайте.
                                    чтобы условие
                                    if (!$resource = $this->modx->getObject($data['class_key'], ['pagetitle' => $data['feed_id']])) {
                                    $resource = $this->modx->newObject($data['class_key']);
                                    }
                                    работало?
                                    Я думал $id = $item->attributes()->id. я получу атрибут оффера.
                                    $id = $xml->offer['id'];
                                    или
                                    $id = $item->offer['id'];
                                      Артур Шевченко
                                      25 августа 2023, 21:38
                                      0
                                      if (!$resource = $this->modx->getObject($data['class_key'], ['feed_id' => $data['feed_id']])) {
                                      $resource = $this->modx->newObject($data['class_key']);
                                      }
                                        Сергей С
                                        25 августа 2023, 21:53
                                        0
                                        Даже так? Благодарю Артур, большой вы человек!
                    Дмитрий
                    11 сентября 2023, 20:16
                    0
                    Привет, Артур! Спасибо тебе за твои труды. Есть вопрос по п.3: Импорт опций товаров из тэгов «param» (опции создаются автоматически). Правильно ли я понимаю, чтобы сопоставить уже созданные опции на своём сайте с param из xml, нужно переименовывать param'ы в файле xml? или это можно сделать через importconfig.inc.php? Если в будущем добавлять новые товары из того же xml, то придется каждый раз переименовывать все param?

                    И второй вопрос: тут уже задавался про обновление по id из фида, но я бы хотел обновлять товары не по id, а по vendorCode, для этого у меня в каждом товаре создано дополнительное поле sku. Как можно поменять обновление по этому ключу?
                      Артур Шевченко
                      11 сентября 2023, 21:41
                      0
                      Скрипт был рассчитан на импорт товаров на пустой сайт, поэтому опции и категории создаются автоматически. Если опции уже есть на сайте, то param должны иметь такие же имена как и ключи у опций на сайте. Если param имеют другие имена, то надо модифицировать скрипт, а именно метод importProducts.

                      Не знаю кто пустил слух, что товары импортируются по feed_id, на самом деле они импортируются по pagetitle, по feed_id определяется родитель. Чтобы сделать импорт товаров по sku, а импорт категорий по pagetitle, надо модифицировать функцию manageResource.
                      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                      43