Честно говоря у меня противоречивые мнения относительно всех возможностей которые предоставляет MODX Revolution. Но факт, что эта CMF обладает огромнейшим потенциалом нельзя скрыть. Многим веб-разработчикам данная информация может показаться неинтересной в виду того, что у меня специфичные задачи. Кому-то — эта информация покажется интересной, но не более того, т. к. на практике применять врятли придется. Что ж, в любом случае я расскажу свою историю а там мало ли…
Еще раз повторюсь, что с revolution я до этих пор никогда не работал, а растягивать проект особо не хотелось, поэтому все решения были сделаны впопыхах. Скорее всего, я что-то делал не так. А что именно — хотелось бы узнать в комментариях.
Для начала поставим задачу. Итак, имеется несколько доменов. Каждый домен это набор страниц независимых друг от друга, т. к. нет меню, списка статей, авторизации и т. п. Стандартных решений. т. е. 1 страница это конкретное описание чего-либо: подписка на рассылку, описание товара, письма которые получают подписчики и т.п. Но есть одно существенное НО — на этих самых самостоятельных страницах по принципу кольца предлагается какой-либо товар характеризующийся таким параметрами, как: цена, ссылка на оформление заказа, фотография товара. Периодически значения данных параметров меняются (цена по акции, новая версия товара, цена для сплит-теста и т. д.).
Сразу же приходит на ум следующее решение
Решение 1. Выделяется один основной домен (акцептор) на котором создается каталог товаров. Акцептор не обязательно, афишировать. Он может быть и техническим. В нужных местах с доноров вставить запрос данных с акцептора. PROFIT.
Это стандартное решение, которые приходит на ум, но т. к. у нас все-же речь идет про MODX и я очень хотел попробовать мультисайтовость в revolution ветке, то продумал следующее решение:
Решение 2. Создам базу в которой буду хранить свой mini каталог товаров. А далее, банальным select … from … where … достану в нужный момент данные. По сути это решение производное от первого решения, но все-же немного другое. Помимо всего прочего, при мультисайтовости в revolution можно было использовать одни и картинки/шаблоны многократно.
Начало реализации 2 решения стандартное: контексты → параметры контекстов → создание часто используемых шаблонов → загрузка картинок и создание документов.
Когда дело дошло до создания той самой базы пришло понимание — с наскоку свой компонент я не напишу. Тем более, для решения этой задачи у меня было всего пару часов. Поэтому немного нервно покурив решил воспользоваться наборами параметров.
Стандартный и «правильный» подход тут следующий: для товара Х создается набор Х с параметрами A,B,C. Затем создается сниппет GetParam следующего содержания
<?php
if(isset($$key)){
return $$key;
}
к которому добавляется тот самый набор Х. Затем, в нужном месте страницы вставляется вызов [[!GetParam@X? &key=`A`]]
Все работает как нужно, но т. к. проект делается для себя. И в будущем, когда будет готов компонент не хочется тратить время на переделывание таких вызовов для работы со своей базой. Поэтому я набросал сниппет извлекающий данные через объекты. Таким образом вызов у меня теперь выглядит так:
<?php
$id=isset($id)?$id:'';
$param=isset($param)?$param:'';
if($id!='' && $param!=''){
$propSet = $modx->getObject('modPropertySet',array('name'=>$id));
if($propSet!==NULL){
$value = $propSet->getProperties();
return isset($value[$param])?$value[$param]:'';
}
}
return '';
Пока я разбирался с этой основной задачей нашел «элегантный» способ организовать сплит-тест 2 страниц средствами движка. Знатоки revolution уже наверное догадались, что речь пойдет про документы типа «символическая ссылка». И как можно догадаться, в то поле, куда вписывается ID страницы я вставил вызов сниппета.
<?php
$list=isset($list)?explode(',',$list):array();
$id='';
if(isset($cookie) && isset($_COOKIE[$cookie])){
$id=$_COOKIE[$cookie];
}
if($id==''){
$id=array_rand($list);
if(isset($cookie)){
setcookie($cookie,$id,time()+365*24*3600);
}
}
return $list[$id];
Все бы здорово, но данный подход приемлем только в случае, если у нас шаблон «символической ссылки» совпадает с шаблоном реального документа. А это невозможно, если сравниваются 2 документа абсолютно разные по дизайну. Поэтому в некоторых случаях приходилось обходиться всем известным типом ресурса «ссылка». Соответственно вызов сниппета меняется на [[~[[!Random? &list=`86,49` &cookie=`vsapns`]]]]
Но тут есть маленькая хитрость. На вкладке настройки у документа типа «ссылка» появляется поле в котором указан header отправляемый при запросе этого документа. По умолчанию там написано HTTP/1.1 301 Moved Permanently. Меняем его на HTTP/1.1 307 Temporary Redirect. Это необходимо для того, чтобы по завершению сплита пользователь принял участие в новом сплите. А не редиректился на старую оттестированную страницу.
Статья получается длинная, а практических советов мало. Поэтому без лирики перейдем сразу к favicon.ico. Всем известно, что если на странице html коде явно не указан адрес иконки, то браузер ее попытается загрузить по адресу /favicon.ico Есть решение, если не хочется отдавать 404 ошибку или одну и ту же иконку для всех сайтов.
Алгоритм следующий
- Создаем в админке новый тип содержимого ICO с расширением файла .ico и MIME типом image/x-icon
- В нужном контексте создаем новый статический ресурс
-
- Местонахождение содержимого: встроенный
- Тип содержимого: ICO
- Тип ресурса: статичный ресурс
- Псевдоним: favicon
- Статический ресурс: указываем путь к нужной favicon
Таким образом, по адресам test1.example.com/favicon.ico и test2.example.com/favicon.ico мы получаем разное содержимое. Аналогично делается и с robots.txt. Только там тип ресурса выбирается text.
т. к. шаблонов для страниц и картинок у нас не много и они используются на всех доменах в данной инсталляции было решено грузить всю статику с определенного домена. Для этого я набросал небольшой плагин к которому необходимо создать параметр StaticNewUrl со значением домена с которого будем грузить статику.
if($modx->event->name=='OnWebPagePrerender'){
$html = &$modx->resource->_output;
$replaceD=array();
preg_match_all('#src=(?:"|')(.*?)>#',$html,$matches);
foreach($matches[1] as $item){
if(substr($item,0,1)!='/'){
continue;
}
if(substr($item,0,2)!='//'){
$replaceD[md5($item)]=$item;
}
}
preg_match_all('#<link.*?href=(?:"|')(.*?)>#',$html,$matches);
foreach($matches[1] as $item){
if(substr($item,0,1)!='/'){
continue;
}
if(substr($item,0,2)!='//' && substr($item,0,12)!='/favicon.ico'){
$replaceD[md5($item)]=$item;
}
}
array_unique($replaceD);
foreach($replaceD as $item){
$html=str_replace($item,$StaticNewUrl.substr($item,1),$html);
}
}
Ну а раз взялись за оптимизацию загрузки статики, то грех не перенести с evolution ветки
if($modx->event->name=='OnWebPagePrerender'){
$flag=true;
if(isset($tvHtmlInLine) && (int)$tvHtmlInLine>0){
$id = $modx->resource->get('id');
$tvs = $modx->getObject('modTemplateVarResource',array('tmplvarid'=>(int)$tvHtmlInLine, 'contentid'=>$id));
if($tvs && 0==$tvs->get('value')){
$flag=false;
}
}
if($flag){
$html = &$modx->resource->_output;
$html = preg_replace('|s+|', ' ', $html);
$html = str_replace('> <','><',$html);
}
}
Правда с установкой тут все немного сложнее, т. к. придется создать TV параметр. Хочу обратить внимание, что плагин вытягивает контент и на страницах с шаблоном blank. Поэтому для целей, где используется данный шаблон и нельзя вытягивать контент в 1 строку (файл robots.txt, например) я создаю новый одноименный шаблон с содержимым [[*content]]. В общем кому интересны подробности — велком в личку или спрашивайте прямо в комментариях.
В заключении хочу поделиться небольшим сниппетом для тех, кто любит на сайте вставлять копирайты с текущей датой.
<?php
//Example: [[Copyright? &date=`2010` &sep=`-`]]
if(!isset($time)){
$time=time();
}
$now=date($format,$time);
$out='';
if(isset($date) && $date!=$now){
$out.=$date;
if(isset($sep)){
$out.=$sep;
}
}
$out.=$now;
return $out;
Автор: Agel_Nash