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 — псевдоним ресурса (должен совпадать с тем, что уже есть у ресурса).​

Остальные поля указываете по необходимости: стандартные поля (pagetitle, longtitle, description, content, и т.д.) и/или TV-поля вида tv1, tv2 и т.п., где цифра — это 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 — которое поможет оживить формы сайта.

Создание страниц ошибок 404, 401, 503 в MODX
← Предыдущая статьяСоздание страниц ошибок 404, 401, 503 в MODX
Следующая статья →MODX pdoTools — дополнение для быстрой выборки страниц и пользователей сайта
MODX pdoTools
Поделиться с друзьями
Алексей

Веб-дизайнер и 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