Сегодня выкладываю новый модуль modRedirect. В целом считаю его с точки зрения полезности весьма сомнительным, но с точки зрения сложности - довольно коварным. Процесс написания его был что-то вроде нового исследования MODX-а.
Для начала цель: бывает так, что уже имеющийся документ переносится в другой раздел, или меняется его алиас, или меняется с обычного документа на контейнер, в общем что угодно, что в итоге приводит к тому, что УРЛ страницы меняется, а если это контейнер и поменялся его алиас, то меняются УРЛы и всех вложенных документов. Наверняка многие с этим сталкивались. И вот идея возникла написать такой модуль, чтобы он отслеживал эти меняющиеся УРЛы, записывал их в таблицу, и при запросе к несуществующей странице проверял наличие запрошенного адреса в таблице, и если находил, то редиректил бы на актуальную страницу 301-ым редиректом. Идея, собственно говоря, весьма простая, а вот реализовать это оказалось гораздо сложнее чем показалось изначально, и это даже при том, что еще не все 100% случаев изменения УРЛов отслеживаются... У меня это заняло часов пять, что по сути очень много. Под катом детали.
Итак, самая главная проблема во всем этом - невозможность вклиниться в метод modResource::save(). То есть если бы мы могли вклиниться непосредственно в момент сохранения конечного объекта документа, то мы могли бы перед сохранением проверить изменился ли у него УРЛ перед записью или нет, и если да, то выполнили бы нужные нам действия. А этого сделать нельзя. Можно только навесить плагин на событие сохранения документа в принципе, но это раньше, чем сам процесс записи изменения в БД, и помимо всего прочего, могут даже после этого сработать другие плагины, которые произведут еще какие-нибудь модификации с объектом документа. Здесь еще проблема в том, что если мы, к примеру, выполним
$o = $modx->getObject('modResource', $id);
$o->alias = 'new alias';
$o->save();
то здесь вообще никакие плагины не работают, так что на это мы вообще никак не можем среагировать.
Вторая проблема заключается в том, что при изменении алиаса раздела, поменяются УРЛы и всех вложенных документов, то есть изменения появятся не в одном документе, а во всех вложенных.
Вот, собственно, я и попытался написать модуль, который бы отслеживал хотя бы стандартные события изменений документов, и сохранял старые УРЛы для последующих редиректов, если документ не найден. Сейчас есть как минимум один вариант, когда он не спасает: когда вы создаете новый документ в документе, который не является контейнером (то есть isfolder != 1), то при сохранении родительский документ превращается в контейнер, и УРЛ этого родителя уже заканчивается на container_suffix, а не на .html, к примеру, и вот это изменение родителя не будет зафиксировано. Поэтому сначала меняете документ-родитель, установив ему галочку Контейнер (и для него тогда будет сохранен старый УРЛ), и только потом создаете дочерний документ.