Ждем нового MODX-а, где будет принципиальная переработки системы модулей. Джейсон над ней во всю работает.
В этом плане круто было бы пакеты первести на Composer. Там и гит без геммороя, и другие интересные вещи. И тестить там просто — указал в конфиге композера ветки у нужных проектов, собрал, погонял, обратно в мастер переключился. Но это реально только в новой версии, на revo такое сложно сделать.
Сразу отмечу, что этот топик — начало цикла статей, в которых я буду описывать реальный опыт переноса большого магазина с Эво на Рево, так что не редко темы будут казаться не законченными, продолжение в новых топиках.
Начали разработку нового крупного магазина, точнее перенос имеющегося с MODX Evolution на MODX Revolution с полным обновлением функционала и дальнейшей его доработкой.
Исходные данные: интернет-магазин на 31 000 товаров (документов), 275 000 TV-параметров (MODX Evolution).
Наверняка многих заинтересует информация по объему кеша и т.п. Так вот, размер кеш-файла — почти 10 метров. При этом сейчас переношу копию сайта на modxcloud.com (да, там и Эво-сайты можно разворачивать, как и любые другие, это же просто хостинг, просто плюшки под Рево всякие). Так вот, сайт просто не запустился. Причина? 128 метров памяти, выделенных под php, не хватает этому сайту даже для запуска. Критической ошибкой о нехватке памяти разваливается на 75000-ой строке (всего в кеше порядка 100 000 строк). А ведь это пока еще чтение кеша. Какого-то объема памяти еще понадобилось бы MODX-у на полную обработку запроса.
Помогло только ручное удаление часть кеша. Тогда сайт запустился. Но теперь кеш очищать нельзя, а то опять сайт развалится :-)
По поводу производительности данного сайта: среднее время открытия страницы: 1-2 сек., поиск по каталогу несколько секунд, не редко больше 10-ти секунд.
Перенос сайта.
Перенос на Рево выполнял уже обкатанным методом: modxclub.ru/blog/dokumentatsiya-dlya-spetsialistov/89.html
На выходе мы уже имеем все исходные шаблоны, TV-параметры, сниппеты, чанки и т.п. Файлы картинок и т.п. мы просто копируем из папки старого сайта, благо картинки находятся как положено в assets/images/.
Обновление УРЛ-ов.
Первая сложность здесь — это обновление УРЛ-ов документов. В MODX Evo не было такого поля в документах, как uri, то есть система УРЛов в Эво и Рево отличается. После переноса контента с Эво, колонка uri в modx_site_content у нас пустая, то есть адрес страница как бы и не имеет.
Наша задача здесь — обновить УРЛы документов. Эту процедуру я описывал здесь: modxclub.ru/blog/dokumentatsiya-dlya-spetsialistov/90.html
Кстати, сразу отмечу, что при большом количестве документов имеет смысл поставить cacheOptimizer и отключить кеширование карты ресурсов как минимум на время проведения этой операции, так как процедура и без того тяжелая, а плюс к этому MODX при очищении кэша будет пытаться сгенерировать всю карту ресурсов занова, и сервер будет плакать. При этом процедура обновления тогда будет отличаться (то есть скрипт отличаться). Дело в том, что при отключенной карте ресурсов при обновлении uri ресурса MODX, способен разобраться только с uri текущего ресурса, но не может обновить uri дочерних ресурсов, так как для них он пытается uri получить из карты ресурсов, а ее у нас нет. То есть за раз обновляет только один документ. Так что лимиты в скрипте получения документов без uri надо устанавливать больше. То есть сколько получит документов, столько и попытается за раз обновить. При этом выполняется относительно быстро (1000 документов обновляет где-то за минуту-полторы). Вот скрипт:
<?php
$modx->setLogLevel(1);
ini_set('max_execution_time', 3200);
ignore_user_abort(true);
$q = $modx->newQuery('modResource', array(
'uri' => null,
));
$q->select(array(
'modResource.*',
));
$q->limit(1000);
$q->sortby('id');
$s = $q->prepare();
$s->execute();
foreach ($s->fetchAll(PDO::FETCH_ASSOC) as $data) {
$modx->error->reset();
$modx->runProcessor('resource/update', $data);
}
print "ok";
Относительно кеша мы еще поговорим дальше не раз.
Все, обновил все 30000+ документов. Часа 2 заняло. Само собой не все два часа я трудился, просто время от времени скрипт перезапускал.
После всей этой процедуры имеет смысл полностью очистить папку кеша по ряду причин.
Проблема два — большой файл кеша.
После полного прогона УРЛов и генерации файла кеша с картой ресурсов, файл кеша весит почти 5 метров, а php требует для свой работы 36 метров с ходу. Это конечно меньше, чем Эво желает, но все равно не айс. Тем не менее, у нас есть пространство для маневров: ведь основной фишкой Рево-2.2.7 была как раз возможность частичного отключения кеширования карты ресурсов (системная настройка cache_alias_map). Сейчас ей и воспользуюсь, то есть отключу ее. Результат: кеш файла сократился до 700 кило, а потребление памяти до 10-ти метров. Результат очевидный!
В общем, проблема с кешем тоже решена (10 метров против 156-ти, по-моему очень хороший результат). Кто там говорил, что Эво легкая, а Рево тяжелая? Всегда говорил — голая Рева конечно тяжеловатая, но она горизонтально масштабируется, а Эва вертикально. Все, что вешается на Эво, все сверху ложится, а в Рево можно аккуратно по полкам разложить. Это я еще не проводил тюнинг, ведь и документы можно раскидать по разным контекстам, так, что кеш еще в разы упадет. Но пока это не требуется.
На сегодня все. Много всего интересного еще будет в следующих топика. Это, так сказать, начало еще.
Довольно часто всплывает такой вопрос: перенесли сайт на новый движок, закинули через phpmyadmin документы, а uri пустые. Или вообще раньше не использовались ЧПУ, а сейчас захотелось, такое вот критическое обновление. В общим причин много бывает, да и не суть. Топик о том, как массово обновить uri документов на MODX Revolution через API MODX-а.
Исходные данные: сайт с кучей ресурсов без актуального uri.
Цель: обновить все документы, чтобы у всех появились uri.
Для начала надо еще один момент оговорить: у вас уже на сайте все документы могут иметь алиасы, а могут быть и без алиасов. То есть если алиасов нет, и надо еще чтобы они так же были автоматически созданы, то надо включить автоматическую генерацию алиасов. А если хотите еще и транслитерацию автоматическую, то надо еще, к примеру, поставить и настроить translit. Но это уже отдельная тема и по ней инфу можно найти в других топиках.
Итак, чтобы у нас сгенерировались новые uri, нам всего лишь надо обновить ресурсы. И лучше это делать не через ->save(), а именно через процессор resource/update, так как только там полный набор всех проверок. И этот способ годится и для обновления групп ресурсов, TV-параметров и т.п. (само собой с некоторыми модификациями).
Вот небольшой скрипт, который можно выполнять через Console.
<?php
$q = $modx->newQuery('modResource', array(
'parent:IN' => array(0), // Перечисляем id разделов, если надо, но если документов не очень много, то можно с корня
));
// Это условие указывает поиск только документов без uri
$q->andCondition(array(
'uri' => null,
'OR:uri:=' => '',
));
// Вот этот вывод полезен, если убрать условие родителя
print "Всего найдено документов по условию: " . $modx->getCount('modResource', $q);
// Сортируем по id
$q->sortby('id');
// Указываем лимит
$q->limit(1);
foreach ($modx->getCollection('modResource', $q) as $doc) {
$modx->error->reset();
$modx->runProcessor('resource/update', $doc->toArray());
if ($modx->error->hasError()) {
print_r($modx->error->getErrors());
}
}
Здесь очень важный момент: $q->limit(1);
Лучше именно один и использовать. Объясню. Когда вы отправляете на обновление ресурс, обновляется не только он, но и дочерние. Потому если вы обновляете в корне сайта какой-то документ-раздел, тогда и внутренние тоже получат новые uri.
Но после того, как пройдете все документы первого уровня, на всякий случай удалите условие поиска по родителю, оставив только условия не заполненного uri, и тогда если что-то еще останется и процесс не очень активно будет протекать, можно будет увеличить лимит выборки.
Сегодня проводил обновление сайта на MODX Revolution 2.0.8 до версии 2.2.5 и хочу поделиться опытом, может кому пригодится.
Для начала проблема: нельзя 2.0.8 обновить сразу до версии 2.2.5 в автоматическом режиме (если у кого-то есть другое мнение, с радостью выслушаю). Со времен 2.0.х не только удалили много деприкативного кода, так еще и добавили много всего, и изменения коснулись не только файловой системы, но и в базе данных много изменений появилось.
В связи с этим увидел только один вариант: развернуть новый сайт и перенести туда все из старого. И как оказалось, это не так уж и сложно оказалось сделать. Здесь вам phpMyAdmin в помощь. Но успех переноса обратнопропорционален привычке вносить изменения в сторонние пакеты (в том числе и в базовые настройки пакетов).
Итак, постараюсь вкратце описать принцип.
Последовательность действий такая:
1. Обязательно делаем резервные копии сайта и базы данных.
2. Создаем новый сайт и инсталлируем последнюю версию MODX Revolution (кстати, при переносе я очень оценил тот факт, что Рева ставится голая, без всяких чанков, сниппетов и т.п., так как нам предстоит перенести все свое, и нам вообще не нужны конфликты в базе данных).
3. В базе данных нового сайта удаляем базовый шаблон из таблицы site_templates.
Все, заготовка у нас есть. Дальше нам надо перенести все с прошлого сайта.
4. Копируем все содержимое папок /assets/, /core/components/ (и своих папок, если вы любите выходить за рамки системы).
5. Вручную копируем все содержимое таблиц с шаблонами, чанками, сниппетами, тв-параметрами, группами пользователей и т.д. (кроме таблиц системных событий, шаблонов политик безопасности и т.п., если вы туда ничего не дополняли). Это самый сложный пункт, требующий навыков работы с БД, и знание структуры БД MODX. Его опишу подробней дальше.
6. Инсталлируем последние версии используемых ранее пакетов.
7. Копируем содержимое таблиц инсталлируемых пакетов. К примеру у меня был установлен Gallery, у него есть свои таблицы. Но в принципе я его таблицы сразу перенес, еще до установки пакета, и проблем не возникло, так как при установке пакета имеющиеся таблицы не затирались.
8. Может понадобиться обновление документов, если у вас ЧПУ и простое обновление кеша сайта не помогло. Но если такое и случится, напишите циркулярную функцию, которая пройдется по всем документам и с полученными объектами выполнит save();
Если уж вообще не захочется заморачиваться, то можно вот такой простой код выполнить:
$docs = $modx->getCollection('modResource');
foreach($docs as &$doc){
$doc->save();
}
Вот вроде бы и все. Конечно надо брать во внимание, что могут быть использованы сторонние пакеты, не совместимые с последней версией Ревы, и т.п., но это может показать только вскрытие. Мой сайт двухгодичной давности переехал без особых проблем. Главное не перепутать местами пункты 5 и 6.
Итак, по переносу содержимого таблиц из прошло базы. Здесь главное написать запросы с нужным количеством колонок, так как многие таблицы будут отличаться (в новых таблицах появились новые колонки, а какие-то удалили (как колонки haskeywords, hasmetatags в таблице site_content и т.п.)).
Здесь можно использовать вот такую последовательность:
1. Открываем целевую таблицу в новой базе данных в phpMyAdmin (или кто что использует, но способ описываю для pma).
2. Нажимаем ссылку insert (чтобы вставить новую запись), и не заполняя поля, жмем GO. pma вставит пустую запись и покажет выполненные запрос со всеми колонками. (показываю на заполненной таблице, потому у меня не ноль записей. У вас таблица должна быть пустая в новой базе.).
Все, мы получили список всех колонок в новой таблице.
3. Удаляем новую запись, она нам вообще не нужна.
4. Теперь сформируем заготовку на копирование данных. Общая структура запроса будет такая: insert into table (COLUMNS) select COLUMNS from old_db.table;
Число вставляемых колонок, и извлекаемых из старой таблицы колонок должно совпадать. Так же и извлекаемые колонки, и вставляемые должны присутствовать в обеих таблицах. Колонок может быть меньше, чем в целевой таблице, но не больше.
Итак, дальнейшими манипуляциями мы получим запрос с максимальным числом колонок из старой таблицы, которые есть в новой таблице.
Вот у нас запрос, который мы получили при вставке пустой строки:
INSERT INTO `modx_site_content`
(
`id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`, `alias`,
`link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`, `isfolder`,
`introtext`, `content`, `richtext`, `template`, `menuindex`, `searchable`, `cacheable`,
`createdby`, `createdon`, `editedby`, `editedon`, `deleted`, `deletedon`, `deletedby`,
`publishedon`, `publishedby`, `menutitle`, `donthit`, `privateweb`, `privatemgr`,
`content_dispo`, `hidemenu`, `class_key`, `context_key`, `content_type`, `uri`,
`uri_override`, `hide_children_in_tree`, `show_in_tree`, `properties`
)
VALUES (
NULL, 'document', 'text/html', '', '', '', '', '', '0', '0', '0', '0', '0',
NULL, NULL, '1', '0', '0', '1', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '',
'0', '0', '0', '0', '0', 'modDocument', 'web', '1', NULL, '0', '0', '1', NULL
);
Здесь у нас уже перечислены все колонки из новой таблицы. Копируем названия этих колонок и формируем такой запрос:
INSERT INTO `modx_site_content`
SELECT
`id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`, `alias`,
`link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`, `isfolder`,
`introtext`, `content`, `richtext`, `template`, `menuindex`, `searchable`,
`cacheable`, `createdby`, `createdon`, `editedby`, `editedon`, `deleted`,
`deletedon`, `deletedby`, `publishedon`, `publishedby`, `menutitle`,
`donthit`, `privateweb`, `privatemgr`, `content_dispo`, `hidemenu`,
`class_key`, `context_key`, `content_type`, `uri`, `uri_override`,
`hide_children_in_tree`, `show_in_tree`, `properties`
FROM old_db.modx_site_content
Если сейчас выполнить этот запрос на целевой базе, то мы получим ошибку Unknown column 'uri' in 'field list'. Эта ошибка говорит о том, что в таблице, из которой мы пытаемся извлечь записи, нет колонци uri (ее ведь действительно не было в версии 2.0.8). Хорошо, удаляем эту колонку из списка в запросе и опять выполняем запрос. Следующая ошибка: Unknown column 'uri_override' in 'field list'. Делаем то же самое, и так пока не уберем из списка все колонки, которых нет в старой таблице.
Так мы будем делать, пока не получим новое сообщение: Column count doesn't match value count at row 1.
Это сообщение говорит о том, что количество колонок в целевой таблице и в извлекаемой, не совпадает (именно в рамках запроса). Сейчас получается то, что запрос из старой таблицы выполняется нормально, но данные в таблицу не могут попасть, так как количество колонок не совпадает, а явно колонки мы не объявили.
Для этого мы просто копируем извлекаемые колонки и вставляем их в скобочки за названием целевой таблицы. Запрос получается такой:
INSERT INTO `modx_site_content` (
`id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`,
`alias`, `link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`,
`isfolder`, `introtext`, `content`, `richtext`, `template`, `menuindex`,
`searchable`, `cacheable`, `createdby`, `createdon`, `editedby`, `editedon`,
`deleted`, `deletedon`, `deletedby`, `publishedon`, `publishedby`,
`menutitle`, `donthit`, `privateweb`, `privatemgr`, `content_dispo`,
`hidemenu`, `class_key`, `context_key`, `content_type`
)
SELECT
`id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`, `alias`,
`link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`, `isfolder`,
`introtext`, `content`, `richtext`, `template`, `menuindex`, `searchable`,
`cacheable`, `createdby`, `createdon`, `editedby`, `editedon`, `deleted`,
`deletedon`, `deletedby`, `publishedon`, `publishedby`, `menutitle`, `donthit`,
`privateweb`, `privatemgr`, `content_dispo`, `hidemenu`, `class_key`,
`context_key`, `content_type`
FROM old_db.modx_site_content
Все, выполняем этот запрос и все должно быть ОК.
Таким образом придется сделать на всех целевых таблицах. Кстати, вот такая хитрость еще есть, чтобы перенести свои данные в таблицы, в которых есть уже данные (Например, перенести свои шаблоны политик безопасности). Для этого надо сформировать такой запрос: insert ignore into table (coluns) select (columns) from old_db.table;
При таком запросе ошибки, возникающие при конфликтах первичных ключей, не будут прекращать весь процесс копирования, а будут игнорироваться, в результате чего будут вставлены только уникальные записи. Но важно понимать, что для этого должны быть настроены первичные (или хотя бы просто уникальные) ключи на целевых таблицах.
Попробую перечислить все целевые таблицы (если у вас в старой базе на каких-то таблицах нет записей, само собой их пропускаем):
- access_resources
- access_resource_groups
- access_templatevars
- categories
- categories_closure
- context (только если у вас больше контекстов, кроме web и mgr)
- context_setting
- lexicon_entries
- membergroup_names
- member_groups (эти две таблицы только если дополнительно настраивали политики безопасности)
- namespaces (проверьте данные записи на предмет абсолютных путей, и если что, поправьте пути с использованием MODX-переменных {core_path} и т.п.)
- property_set
- site_content
- site_htmlsnippets
- site_plugins
- site_plugin_events
- site_snippets
- site_templates
- site_tmplvars
- site_tmplvar_access
- site_tmplvar_contentvalues
- site_tmplvar_templates
- system_settings (только обновление записей и очень аккуратно. Если в старом сайте особо ничего не настраивали, лучше этого вообще не делать и возможные настройки выполнить вручную).
- users
- user_attributes
- user_group_roles (тоже только в случае если проводились настройки политик безопасности)
- user_settings
- workspaces (тоже проверить пути)
Вот вроде бы и все.
Пожалуйста
Спасибо большое
Нет, особо посоветовать ничего не могу. Сам во всем разбирался сам, и если где-то и есть что-то, то я просто могу об этом не знать.
А так вот раздел по xPDO есть: community.modx-cms.ru/blog/modx-xpdo/
Там несколько статей, но очень полезные.
Разобрался, спасибо за ответы.
Николай, Вы не могли бы порекомендовать хороший ресурс, желательно русскоязычный, по MODX API?
Мне попадаются либо самые азы, либо дебри, и все как-то разрозненно, по кусочкам.
Я с удовольствием посетил бы Ваши уроки, но в данный момент я сам прохожу программу Нарконон, а выезды у нас не практикуются, поэтому это пока в планах на недалекое будущее, а руки уже чешутся, есть несколько идей, хочется попробовать. Сам я почти 20 лет писал на С++, Delphi, в т.ч. базы данных, немного на Perl. PHP знаю пока поверхностно, но с ним проблем никаких. Хотелось бы почитать что-нибудь последовательное, от простого к сложному, и желательно на русском. Буду очень признателен за ответ.
Разобрались со своими редиректами? Как я вижу, с .html нормально редирект идет.
www.narcononmsk.ru/syin-narkoman?q=syin-narkoman.html, хотя изначально там переход
Здесь у вас чуть сложнее будет. Вы настроили серверные правила редиректа. То есть при обращении к страницам с .html, дело до MODX-а еще не доходит. Сервер видит запрос к документу с суффиксом .html и редиректит по правилу. Далее уже вступает в дело MODX.
Но это правило анализирует имя файла, к которому идет запрос, а не параметры. В запросе www.narcononmsk.ru/syin-narkoman?q=syin-narkoman.html syin-narkoman.html — не имя файла, а значение переменной q. Сервер его не проверяет и не редиректит. То есть он отдает запрос MODX-у. Но q — это зарезервированная переменная MODX-а, которую он использует для определения запрошенного документа, и хотя у вас запрошена страница syin-narkoman, которая для MODX-а по сути имеется, переменная q перебивает этот адрес, заставляя MODX думать, что запрошена страница syin-narkoman.html. Но этой страницы для MODX-а не имеется (вы сами у себя удалили суффиксы .html). Про правила редиректа на стороне веб-сервера MODX ничего не знает, и совершенно их не использует, а отдает законную 404-ую в ответ на ненайденную страницу.
Резюме: безусловно надо еще перестраховываться с редиректами на стороне самого MODX-а. Создайте плагин на событие OnPageNotFound, и настройте те же правила на регулярках, только на php, используя переменную $modx->resourceIdentifier и метод $modx->sendRedirect($url);
Примерный код плагина:
if(!empty($modx->resource) && is_object($modx->resource)){ return; } if(preg_match('/(.+)\.html([\?\#$]?.*)/', $modx->resourceIdentifier, $match)){ $modx->sendRedirect("{$1}{$2}"); return; }