Многие разработчики, создавая свой модуль, сталкиваются с необходимостью предоставить этот модуль пользователю максимально удобным способом.
Начиная с версии 2.8.3, в системе появилась возможность максимально полного переноса информации (например, отдельного модуля, со всеми необходимыми данными и файлами) из одной системы в другую.
Сейчас мы рассмотрим пример того, как можно получить все необходимые файлы и данные для переноса разработанного модуля (на примере модуля "Каталог") из девелоперской системы на сайт клиента. В этом примере мы будем создавать папку "export" для экспортируемых файлов и данных (в xml-формате), файл export.php для выполнения соответствующей операции, а также рассмотрим варианты организации импорта.
Получить наглядные примеры скриптов можно, скачав данный архив.
Начнём с файла export.php
В начале, как обычно, подготавливаем скрипт к автономной работе через API UMI.CMS:
<?php
error_reporting(E_ALL);
include "standalone.php";
Начинаем подготавливать данные для настройки экспорта. Во-первых, задаём (и создаём, в случае отсутствия) директорию для сохранения экспортированных файлов (вышеупомянутая папка "export"):
$curr_dir = CURRENT_WORKING_DIR;
$destinationPath = $curr_dir . "/export"; // Задаём директорию для сохранения файлов экспорта.
if (!is_dir($destinationPath)) mkdir($destinationPath, 0777, true);
Создаём экземпляр экспорта и начинаем настройку:
$exporter = new xmlExporter("catalog"); // Задаём идентификатор экспорта
$exporter->setDestination($destinationPath); // Задаём директорию для сохранения файлов экспорта
В переменную "files" помещаем массив из файлов, которые необходимо экспортировать:
/* Функция для рекурсивного выбора файлов из директорий */
function recursiveGlob($pattern = '*', $flags = 0, $path = '') {
$paths = glob($path . '*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
$files = glob($path . $pattern, $flags);
foreach ($paths as $path) {
$files=array_merge($files, recursiveGlob($pattern, $flags, $path));
}
return $files;
}
$php_files = recursiveGlob('*', 0, $curr_dir . "/classes/modules/catalog/");
$ico_files = array($curr_dir . '/images/cms/admin/mac/icons/big/catalog.png',
$curr_dir . '/images/cms/admin/mac/icons/medium/catalog.png',
$curr_dir . '/images/cms/admin/mac/icons/small/catalog.png');
$tpl_files = recursiveGlob('*.tpl', 0, $curr_dir . "/tpls/catalog/");
$xsl_files = recursiveGlob('*.xsl', 0, $curr_dir . "/xsltTpls/modules/catalog/");
/* Выбираем файлы модуля */
$files = array_merge($php_files, $ico_files, $tpl_files, $xsl_files);
$exporter->addFiles($files); // Задаём массив экспортируемых файлов
В переменную "paths" собираем массив из ключей реестра. Для этого воспользуемся классом regedit. В нашем случае это будут ключи, необходимые для работы модуля (//modules/catalog/name, //modules/catalog/func_perms и т.д.):
/*Функция для рекурсивного выбора ключей реестра*/
function getAllRegistryList ($parent_path) {
$paths = array();
$children = regedit::getInstance()->getList($parent_path);
if (is_array($children)) {
foreach ($children as $child) {
$child_key = regedit::getInstance()->getKey($child[0]);
if ($parent_path != "//") {
$child_path = $parent_path . '/' . $child[0];
} else {
$child_path = $child[0];
}
$paths[] = $child_path;
$paths = array_merge($paths, getAllRegistryList($child_path));
}
}
return $paths;
}
/* Выбираем пути к записям реестра */
$paths = getAllRegistryList ("//modules/catalog");
/* Задаём массив путей к записям реестра */
$exporter->addRegistry($paths);
Далее нам необходимо заполнить переменную "types" массивом из id типов данных, которые мы хотим экспортировать. Проще всего это сделать при помощи класса umiObjectTypesCollection:
/* Например, так: */
$typesCollection = umiObjectTypesCollection::getInstance();
$typeId = $typesCollection->getBaseType('catalog', 'object');
$types = $typesCollection->getSubTypesList($typeId);
$types[] = $typeId;
$exporter->addTypes($types);
Теперь, используя массив "types" и механизм Selector, мы сможем легко получить страницы(массив "pages") и объекты(массив "objects"), которые необходимо экспортировать:
/* Выбираем страницы и объекты, относящиеся к выбранным выше типам данных */
foreach ($types as $typeId) {
$sel = new selector("pages");
$sel->types('object-type')->id($typeId);
$pages = $sel->result();
$exporter->addElements($pages); // Задаём массив элементов
$sel = new selector("objects");
$sel->types('object-type')->id($typeId);
$objects = $sel->result();
$exporter->addObjects($objects); // Задаём массив объектов
}
Так как при экспорте данных модуля нам обычно не нужны данные о домене и языке, мы исключаем их из экспорта при помощи метода setIgnoreRelations() класса "xmlExporter":
$saveRelations = array('files', 'templates', 'objects', 'fields_relations', 'restrictions', 'permissions', 'hierarchy', 'guides');
$exporter->setIgnoreRelations($saveRelations);
Завершаем настройку и запускаем экспорт:
$exporter->setShowAllFields(true); // Устанавливаем флаг экспорта всех полей (в т.ч. системных и скрытых)
$dom = $exporter->execute(); // Запускаем экспорт. Результат записываем в переменную
$log = $exporter->getExportLog(); // В этой переменной хранится лог ошибок экспорта (нужна для отладки)
Осталось только сохранить полученные xml-данные (переменная "dom") в файл и можно работать:
/* Сохраняем xml для будущего импорта */
file_put_contents("./export/catalog.xml", $dom->saveXML());
На что следует обратить внимание во избежание проблем с экспортом:
- При экспорте страниц и объектов, в директорию "destinationPath" будут также скопированы
файлы, связанные с этими элементами (например, изображения и файлы из соответствующих типов полей).
Поэтому:
- Запускать скрипт-экспортер следует из корневой директории сайта.
Иначе путь к изображениям\файлам будет некорректным и они не будут скопированы.
- Если при экспорте происходят ошибки - вывести массив с их описанием можно, например,
строкой var_dump($log); в конце скрипта.
- Если папка "destinationPath" создаётся вручную - убедитесь что для неё
выставлены права на запись скриптом.
- Также стоит иметь в виду, что метод "setDestination" не создаёт директорию и
в случае её отсутствия (не создана вручную и не создаётся скриптом) экспорт файлов не будет произведен.
- Для корректной настройки экспорта настоятельно рекомендуем
ознакомиться с возможностями класса xmlExporter.
Итак, данные успешно экспортированы, мы получили папку "export" с необходимым содержимым и можем приступать к процессу импорта данных на другом сайте. Переместив директорию "export" в корень целевого сайта, мы имеем несколько вариантов для импорта. Так как они не слишком отличаются - основные опишем вместе:
1. Мы можем создать файл import.php в корне сайта и внести в него код, описанный ниже
2. Либо мы можем включить этот код в файл install.php самого модуля и получить все данные и файлы сразу при установке модуля.
В первом случае, в файле import.php нам нужно снова подключить standalone.php:
<?php
error_reporting(E_ALL);
include "standalone.php";
$dir = CURRENT_WORKING_DIR . "/export";
Следующий код будет практически идентичен для обоих вариантов. Настраиваем и запускаем импортер:
$importer = new xmlImporter();
$importer->loadXmlFile($dir . "/catalog.xml");
$importer->setUpdateIgnoreMode(); // режим НЕ обновления уже существующих записей
$importer->setFilesSource($dir); // путь до файлов
$importer->execute();
На что следует обратить внимание во избежание проблем с импортом:
- Для вывода сообщений работы xmlImporter'a так же как у xmlEmporter'a существует
свой метод - "getImportLog()".
- Для корректной настройки импорта настоятельно рекомендуем
ознакомиться с возможностями класса xmlImporter.