Скрытый потенциал MODX

от автора

Честно говоря у меня противоречивые мнения относительно всех возможностей которые предоставляет 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`]]

Все работает как нужно, но т. к. проект делается для себя. И в будущем, когда будет готов компонент не хочется тратить время на переделывание таких вызовов для работы со своей базой. Поэтому я набросал сниппет извлекающий данные через объекты. Таким образом вызов у меня теперь выглядит так:

[[!GetParam? &id=`X` &param=`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 страницы я вставил вызов сниппета.

[[!Random? &list=`86,49` &cookie=`vsapns`]]

<?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
  • В нужном контексте создаем новый статический ресурс
    1. Местонахождение содержимого: встроенный
    2. Тип содержимого: ICO
    3. Тип ресурса: статичный ресурс
    4. Псевдоним: favicon
    5. Статический ресурс: указываем путь к нужной 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 ветки

плагин вытягивающий контент в 1 строчку.

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]]. В общем кому интересны подробности — велком в личку или спрашивайте прямо в комментариях.

В заключении хочу поделиться небольшим сниппетом для тех, кто любит на сайте вставлять копирайты с текущей датой.

© 2000—2012

<?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;

ссылка на оригинал статьи http://habrahabr.ru/post/156643/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *