MODX ImportX — импорт и обновление ресурсов

ImportX - импорт ресурсов MODX Revo
ImportX - это бесплатный MODX пакет (работает как на v2, так и на v3), который можно использовать для быстрого создания новых ресурсов из CSV. Так же позволяет обновлять ресурсы. 

Установка ImportX

Установка ImportX стандартная из основного репозитория .

Как работать с ImportX

После активации дополнения в верхнем меню «Пакеты» у вас проявиться пункт «ImportX» переходим в него. На открывшейся странице заходим во вкладку «Базовые настройки» и задаем их.

Настройка ImportX

  • Родитель: укажите id родителя в которого импортировать ресурсы;
  • Опубликован: установите галку если хотите чтобы ресурс был сразу опубликован;
  • Доступен для поиска: установите галку, чтобы импортированные ресурсы были доступны для поиска;
  • Скрыть из меню: установите галку если хотите скрыть импортированные ресурсы из меню.

Переключаемся во вкладку «Ввод CSV» и в поле Чистый CSV, вписываем вот такую конструкцию:

pagetitle;longtitle;description;introtext;menutitle;alias;template;content;tv1 — т.е. перечисляем стандартные поля ресурса (пишите только нужные) + дополнительные поля в формате tv1 — где 1 это id TV поля в админке.

Далее в таком же формате и последовательности указываем контент разделенный через «;». и нажимаем кнопку «Начать импорт».

Импортируем контент при помощи ImportX

После нажатия на кнопку «Начать импорт», компонент проверит данные и если все хорошо произведет импорт.

Консоль ImportX

После закрытия консоли (кнопка «OK»), дерево ресурсов обновляется и у нас появляется в нужном месте новые ресурсы.

Импортированные ресурсы

Как подготовить данные для импорта, чтобы не запутаться

Я обычно подготавливаю все данные для импорта в excel, чтобы не запутаться.

подготовка данных для импорта

Далее копирую это все добро в notepad++ и при помощи поиска и замены (CTRL+H) заменяю широкие пробелы (получившиеся в результате копирования с эксель -между колонками) на знак разделитель.

подготовка данных для импорта в notepad

и получаю подготовленные данные для импорта которые вставляю в поле Чистый CSV. Смотрите чтобы у вас не было лишних разделителей и разделителей в контенте.

данные для импорта в notepad

Обновление существующих ресурсов через ImportX

По умолчанию ImportX создает новые ресурсы при импорте CSV. Но начиная с версии 1.1 компонент умеет не только создавать, но и обновлять уже существующий контент. Для этого нужно изменить одну системную настройку и немного по‑другому подготовить CSV.

Важно! Из коробки на данный момент есть серьезный баг https://github.com/modmore/importX/issues/64 , который делает этот функционал неюзабельным. Частичное решение есть в этом же ишьюсе. Так же смотрите ниже фикс от меня — перед тем как воспользоваться данной функцией)

Включаем режим обновления (update)

  1. Перейдите в «Системные настройки» MODX (верхнее меню «Система» → «Системные настройки»).
  2. В фильтре по пространству имён выберите importx (или в общем поиске начните вводить importx.processor).​
  3. Найдите параметр importx.processor.
  4. Измените его значение с create на update и сохраните.​

После этого ImportX при импорте будет пытаться обновлять существующие ресурсы по их ID, а если ресурс с таким ID не найден — создавать новый.

Обязательные поля при обновлении

В режиме update ImportX ориентируется на ID ресурса, но при этом процессор ресурса требует наличие alias (псевдонима) — без него будет ошибка вида «alias: This field is required». Поэтому для обновления рекомендуется указывать хотя бы:

  • id — числовой ID ресурса в MODX (как в дереве ресурсов);
  • alias — псевдоним ресурса (должен совпадать с тем, что уже есть у ресурса).​

Остальные поля указываете по необходимости: стандартные поля (pagetitlelongtitledescriptioncontent, и т.д.) и/или TV-поля вида tv1tv2 и т.п., где цифра — это ID TV.​

Пример «шапки» и строки для обновления SEO‑полей через TV:

id;alias;tv1;tv2
14;certificates;Seo title;Seo description
  • id — ID ресурса;
  • alias — его alias;
  • tv1 — TV с ID 1 (например, SEO title);
  • tv2 — TV с ID 2 (например, SEO description).

Важно: порядок полей в первой строке (заголовке) должен точно совпадать с порядком значений в каждой строке данных, а количество столбцов одинаково во всех строках.​

Общие правила для CSV при обновлении

Все общие требования к CSV для ImportX остаются такими же, как и при создании ресурсов:​

  • Первая строка — заголовок с названиями полей: id;alias;pagetitle;tv1;tv2 и т.п.;
  • Строки разделяются переводом строки;
  • В качестве разделителя по умолчанию используется ;, но при необходимости можно задать нестандартный (например, ;;;) во вкладке «Ввод CSV», если в контенте встречаются точки с запятой;
  • Количество значений в каждой строке должно совпадать с количеством заголовков;
  • TV указываются в формате tvN, где N — ID ТВ-поля.

Логика работы в режиме update

После переключения importx.processor в update ImportX работает по следующему принципу:​

  • Берёт из CSV строку, читает id ресурса.
  • Пытается найти ресурс с таким ID.
  • Если ресурс найден — обновляет переданные поля (включая TV).
  • Если ресурс по этому ID не найден — создаёт новый ресурс и заполняет переданные поля.​

Поэтому при массовом обновлении:

  • Внимательно проверяйте id, чтобы не перезаписать «чужие» ресурсы;
  • Следите за alias, чтобы он соответствовал уже существующему ресурсу и не вызывал конфликтов.​

Фикс режима обновления ImportX (MODX 2 и MODX 3)

В стандартной версии ImportX режимupdateработает нестабильно: при обновлении ресурсов вы можете получать ошибки по обязательным полям (alias,class_keyи т.п.), а поведение не совпадает с ожидаемым. Ниже — минимальный фикс, который делает обновление предсказуемым и одинаково понятным и в MODX 2, и в MODX 3.

Что будет после фикса

  • Режим create продолжает создавать новые ресурсы из CSV, как и раньше.
  • Режим update обновляет только существующие ресурсы по ID, не пытаясь автоматически создавать новые, если ресурс не найден.
  • В CSV для обновления вам достаточно указать id,alias и нужные поля/TV, остальные данные ресурса сохраняются без изменений.

Пример CSV для обновления SEO‑TV:

id;alias;tv1;tv2 14;certificates;Seo title;Seo description

  • id— ID ресурса в MODX.
  • alias— псевдоним ресурса (берётся либо из CSV, либо из самого ресурса).
  • tv1,tv2— TV‑поля по их ID.

1. Исправляем setDefaultClassKey (важно для MODX 3)

Файл:core/components/importx/processors/startimport.class.php.

В конце класса StartImport есть метод:

protected function setDefaultClassKey(array $record): array
{
    $modxVersion = $this->modx->getVersionData();
    if (version_compare($modxVersion['full_version'], '3.0.0-dev', '>=') && empty($line['class_key'])) {
        $record['class_key'] = 'MODX\\Revolution\\modDocument';
    }

    return $record;
}

Замените строку с ошибочной переменной$line на $record:

protected function setDefaultClassKey(array $record): array
{
    $modxVersion = $this->modx->getVersionData();
    if (version_compare($modxVersion['full_version'], '3.0.0-dev', '>=') && empty($record['class_key'])) {
        $record['class_key'] = 'MODX\\Revolution\\modDocument';
    }

    return $record;
}

Как это работает:

  • В MODX 2.x это условие не срабатывает, поэтому поведение остаётся стандартным: ядро само подставляет modDocument для ресурсов безclass_key.
  • В MODX 3.x метод автоматически подставит MODX\Revolution\modDocument, если вы не указали class_keyв CSV, и тем самым избавит от ошибок при создании/обновлении.

2. Разделяем логику create и update

В этом же файле startimport.class.php заменяем метод process()на обновлённый вариант.

Найдите существующий public function process() и полностью замените его на код ниже:

public function process()
{
    // Получаем и готовим данные
    $this->importX->getData();
    sleep(1);
    $lines = $this->importX->prepareData();

    if ($lines === false) {
        $this->importX->log('complete','');
        return $this->modx->error->failure();
    }

    $this->importX->log('info', $this->modx->lexicon('importx.log.importvaluesclean', ['count' => count($lines)]));
    $resourceCount = 0;

    $processorMode = $this->modx->getOption('importx.processor', null, 'create'); // create или update
    $processor = 'resource/' . $processorMode;

    foreach ($lines as $line) {
        // Для MODX 3 подставляем class_key при необходимости
        $line = $this->setDefaultClassKey($line);

        // ===== РЕЖИМ СОЗДАНИЯ (create) =====
        if ($processorMode === 'create') {
            /** @var modProcessorResponse $response */
            $response = $this->modx->runProcessor($processor, $line);
        }
        // ===== РЕЖИМ ОБНОВЛЕНИЯ (update) =====
        else {
            $id = (int)($line['id'] ?? 0);
            if (!$id) {
                $this->modx->log(modX::LOG_LEVEL_ERROR, 'importX: ID is required for update, skipping row');
                continue;
            }

            /** @var modResource $resource */
            $resource = $this->modx->getObject('modResource', $id);
            if (!$resource) {
                $this->modx->log(modX::LOG_LEVEL_ERROR, "importX: Resource with ID {$id} not found, skipping row");
                continue;
            }

            // Берём все поля из существующего ресурса
            $data = $resource->toArray();

            // Перезаписываем только те поля, которые пришли в CSV (кроме TV)
            foreach ($line as $field => $value) {
                if ($value === '' || $value === null) {
                    continue;
                }
                if (strpos($field, 'tv') === 0) {
                    continue;
                }
                $data[$field] = $value;
            }

            // Обязательные поля
            $data['id'] = $id;

            // alias: либо из CSV, либо текущий alias ресурса
            if (!empty($line['alias'])) {
                $data['alias'] = $line['alias'];
            } else {
                $data['alias'] = $resource->get('alias');
            }

            /** @var modProcessorResponse $response */
            $response = $this->modx->runProcessor('resource/update', $data);

            // Если обновление прошло успешно — обновляем TV по CSV
            if (!$response->isError()) {
                foreach ($line as $field => $value) {
                    if (strpos($field, 'tv') !== 0) {
                        continue;
                    }
                    $tvId = (int)substr($field, 2);
                    if ($tvId > 0) {
                        /** @var modTemplateVar $tv */
                        $tv = $this->modx->getObject('modTemplateVar', $tvId);
                        if ($tv) {
                            $tv->setValue($resource->get('id'), $value);
                            $tv->save();
                        }
                    }
                }
            }
        }

        if ($response->isError()) {
            if ($response->hasFieldErrors()) {
                $fieldErrors = $response->getAllErrors();
                $errorMessage = implode("\n", $fieldErrors);
            } else {
                $errorMessage = $this->modx->lexicon('importx.err.savefailed') . "\n" . print_r($response->getMessage(),true);
            }

            $this->importX->log('warn', $resourceCount.' of ' . count($lines) . ' resources were imported successfully');
            $this->importX->log('error', $errorMessage);

            return $this->importX->log('complete','');
        } else {
            $resourceCount++;
        }
    }

    sleep(1);
    $this->importX->log('info', $this->modx->lexicon('importx.log.complete', ['count' => $resourceCount]));
    sleep(1);
    $this->importX->log('complete', '');
    sleep(1);

    return $this->success();
}

Как этим пользоваться после фикса

  1. В системных настройках установите:
    importx.processor = update— для режима обновления.
  2. Подготовьте CSV минимум с колонкамиidиalias, плюс нужные поля/TV:

id;alias;tv1;tv2 14;certificates;Seo title;Seo description

  1. Запустите импорт как обычно через ImportX.
  2. ImportX:
    • найдёт ресурс с указаннымid;
    • подставит его текущие данные;
    • поверх обновит только те поля, что есть в CSV;
    • отдельно сохранит TVtv1,tv2и т.д. для этого ресурса.

Важно: режим update после этого фикса не создаёт новые ресурсы, если ID не найден — такие строки просто пропускаются с записью в лог. Для создания новых страниц продолжайте использовать режим create и стандартный сценарий из начала урока.

Заключение

ImportX хорош только тем что он бесплатный. Его можно использовать для создания категорий. Импортировать ресурсы с большим количеством контента у меня при помощи него не получилось. Но вы можете попробовать, для этого вам придется задать нестандартный разделитель типа «;;;» и скорее всего уже использовать CSV файл.

Для импорта ресурсов с большим количеством контента, я использую платное дополнение с modstore: GoogleSheets.

Для импорта категорий и товаров в minishop использую msImportExport (не смотря на то что GoogleSheets тоже может это делать). PS. Уже может импортировать ресурсы и т.д.

В следующем уроке разберем дополнение FormIt — которое поможет оживить формы сайта.

Поделиться с друзьями
Алексей

Веб-дизайнер и SEO оптимизатор. Занимаюсь созданием сайтов с 2010 года и их продвижение с 2012 года!

Оцените автора
( Пока оценок нет )
Web-Revenue.ru
Добавить комментарий

  1. Виталий

    В результате тестирования режима обновления нарвался на баг, который делает этот функционал неюзабельным.
    Баг подтвержден в ишьюсах — https://github.com/modmore/importX/issues/64
    Частичное решение есть в этом же ишьюсе, но, имхо, не стоит данный функционал вообще упоминать, как рабочий

    Ответить
    1. Алексей автор

      Честно говоря не пользовался функцией обновления) Посмотрел исходник на гитхаб, увидел косяки, которые правятся в принципе) Добавил в статью фикс этого бага (не испытывал его, но работать должен). Можете протестить и отписаться)

      Ответить
      1. Виталий

        Я пошел немного другим путем — переработал скрипт /core/components/minishop2/import/csv.php.
        Ваше решение протестирую на этой неделе и отпишусь

        Ответить
        1. Алексей автор

          Тоже вариант)

          Ответить
          1. Виталий

            Проверил. Ситуация не изменилась. Дело в том, что в принципе этот процессор «update» как-то странно реализован. Вместо того,чтобы банально обновлять поля ресурса по его id, реализована странная логика. Там куча обязательных полей надо указывать, чтобы все прошло гладко (см. https://webinmind.ru/modx/solutions/working-with-resources/editing-resources-through-the-processor?ysclid=mlmllx4glt511275660). Мне проще было вместо него использовать прямую замену полей через использование связки:
            $res = $modx->getObject(‘modResource’, $data[‘id’]);
            + для основных полей:
            $res->set($field_id, $field_value);
            $res->save();
            + для доп. полей:
            $res->setTVValue((int)$field_tv_id, $field_tv_value); // сохранять ресурс в этом случае не надо

          2. Алексей автор

            Ага, хорошо что поделились. Возможно как нибудь займусь и сделаю форк рабочий и более функциональный

  2. Виталий

    еще момент — в случае обновления, необходимо указывать id ресурса и его alias. Т.е. импорт должен быть вида:
    id;alias;tv1;tv2
    14;certificates;Seo title;Seo description

    Ответить
  3. Виталий

    Если не сложно, добавьте в текст статьи инфо про обновление контента. Для этого необходимо в системных настройках переключить параметр ‘importx.processor’ c ‘create’ на ‘update’. В этом случае уже существующий контент будет обновлен, а не существующий — создан.

    с уважением

    Ответить
    1. Алексей автор

      Не сложно, добавил)

      Ответить
  4. Любовь

    Спасибо! А есть какой-то вариант затем экспортировать это на другой сайт? Который тоже конечно же на modx revo?)

    Ответить
    1. Алексей автор

      Я пользуюсь для таких целей плагином https://modstore.pro/packages/import-and-export/msimportexport

      Ответить