Полосы лендинга как отдельные документы MODX

Возникла задача собирать страницы из полос с возможностью изменения порядка и отключения этих самых полос, добавления новых шаблонов блоков и тд. С мультиполями непонятно, как сортировать, менять порядок вывода и набирать «блоки» в произвольные наборы в каждом разделе, а дерево документов справляется с этим вполне, также для каждого отдельного шаблона одной полосы можно задать свои TV параметры итд. Также решается вопрос о постепенном добавлении новых видов блоков.

Например, на схеме 3 таких блока, каждый со своим щаблоном-оберткой и набором полей или вызовов сниппетов, типа формы(3) или списка дочерних ресурсов(2)



Надо как-то это все добро склеивать в одну страницу. Пока ничего кроме AJAX не придумал, но вроде просто — скормил массив ссылок на ресурсы первого уровня, отсортированные средствами «позиции в меню» и грузи себе. Но это подход — дорогой и не очень производительный. Как делать такую же штуку в статике? То есть склеить уже отрисованные страницы с результатами отработавших сниппетов. Или может я вобще куда-то не туда пошел и всё можно сделать проще?

40 комментариев

avatar
я их через file_get_contents склеивал, который вызывал в кэшированном сниппете.
avatar
Делал так же :)
когда надо было с простого сайта сделать лендинг. А для того что б оставить обратную совместимость добавил разделители что б убирать header и footer )

Но можно использовать чуть по другому не использовать шаблоны, вместо них использовать чанки а в чанке уже выводить инфу что надо
и тогда будет чуть проще.
avatar
С наполнением неинтересно получается
avatar
А наполнять без проблем через документы :) тут вообще не проблема:) при любом подходе.
avatar
Тоже рабочий вариант, но он тоже не изнутри а извне
avatar
что значит «не изнутри, а извне»?))
avatar
Ну, такой же как ajax, а было бы чего-нибудь типа
modx->getRenderedPage(id)
-вот это было бы интересно
avatar
Нет такого :)
avatar
Этот вопрос интересовал меня еще два с половиной года назад — modx.im/blog/questions/1494.html — ничего лучше и гибче в итоге так и не нашлось :) Через кэшированный сниппет, в котором просто собирались страницы через file_get_contents вполне нормально работало.
avatar
Не получается. В сниппет getContent передается &docid, сам сниппет такой:
return file_get_contents($modx->makeUrl($docid));


Вызываю на той странице, которую надо компоновать. Ничего не выводится.
В документе, который надо получить сниппетом — в свою очередь также сниппет, формирующий контент (галерея).

Буду благодарен за пояснение.

P.S. Да, забыл написать — версия MODx новая.
Комментарий отредактирован 2017-02-20 10:46:54 пользователем Aharito
avatar
Попробовать, для начала
$modx->makeUrl($docid, '', '', 'full')
avatar
При таком варианте все виснет — страница фронта грузится бесконечно долго, а админка также виснет, и постоянно в админке надпись «Обработка данных», приходится перезапускать сервер.
avatar
ну значит ошибка на одной из страниц :) попробовать по одному вызывать нужные id, чтобы вычислить «проблемный». В любом случае надо использовать full — чтобы был полный адрес страницы, а не его относительный url :)

$out = file_get_contents($modx->makeUrl('2', '', '', 'full'));
return $out;

$out = file_get_contents($modx->makeUrl('3', '', '', 'full'));
return $out;

$out = file_get_contents($modx->makeUrl('4', '', '', 'full'));
return $out;


и т.п. с нужными docid
avatar
А заодно проверить, что возвращает сам
$modx->makeUrl('2', '', '', 'full')
avatar
Тема хорошая, надо попробовать, но на момент обсуждения ничего подобного не существовало — отсюда и вырос file_get_contents, чтобы можно было гибко шаблонизировать каждый блок ленда — прилепить свои TV, свой шаблон и т.п. к каждому блоку :)
avatar
Я недавно использовал DLTemplate в одном из своих сниппетов, и как раз подумывал о нем в контексте озвученного мной вопроса. Но смущает отсутствие доков.

Придется код разбирать.

Перед разбором кода сразу вопрос )) Для DLTemplate нужно скармливать обязательно ассоциативный массив, а значит, вызывать DocLister в режиме API, а потом json_decode? так иди нет?
avatar
Насколько я понял ваш сниппет landing должен в итоге выглядеть так

<?php
$parent = 5;//сюда родителя блоков вставляем
$out = '';
require_once(MODX_BASE_PATH.'assets/snippets/DocLister/lib/DLTemplate.class.php');
$DLT = DLTemplate::getInstance($modx);
$q = $modx->db->query("SELECT id,template FROM " . $modx->getFullTableName("site_content"). " WHERE published=1 AND deleted=0 AND parent={$parent} ORDER BY menuindex ASC");
while ($row = $modx->db->getRow($q)) {
    $out .= $DLT->renderDoc($row['id'], false, $row['template']);
}
return $out;


создаете папку, куда все нужные блоки и размещаете. id этой папки в parent вставляете, сортировка будет идти по menuindex.
И на самой странице лендинга просто [[landing]]
avatar
Если хотим, чтобы был вызов событий для плагина на каждую «подстраницу» меняем false на true
avatar
Либо, как написано у умных людей

Наиболее удачная практика — создание шаблонизатора в переменной tpl у объекта $modx. Чтобы не делать это постоянно, лучше всего воспользоваться небольшим плагином на событиях OnWebPageInit и OnPageNotFound

require_once(MODX_BASE_PATH.'assets/snippets/DocLister/lib/DLTemplate.class.php');
$modx->tpl = DLTemplate::getInstance($modx);

Тогда в любом сниппете можно будет обращаться

$out .= $modx->tpl->renderDoc($row['id'], false, $row['template']);
avatar
Насколько я понял ваш сниппет landing должен в итоге выглядеть так

Я примерно так же и начал делать, только вместо запроса $q = $modx->db->query(«SELECT делаю вызов ДокЛистера в режиме API. Как раз чтобы всякие сортировки легко задавать и т.д.

Либо, как написано у умных людей

А то, что у умных людей написано — я тоже уже прочитал :))
avatar
Было бы там что разбирать. Для метода parseChunk, да, нужен массив, только не понимаю, при чем здесь DocLister.
avatar
DocLister будет пробегать по «страницам-блокам», из которых нужно сформировать итоговый лендинг.
avatar
Проще сделать tv типа Selector, в котором задавать список блоков, а выводить сниппетом:

//[[landing? &input=`[*blocks*]`]]
$DLT = \DLTemplate::getInstance($modx);
$out = '';
if (!empty($input)) {
    $ids = explode(',',$input);
    foreach ($ids as $id) {
        $out .= $DLT->renderDoc($id);
    }
}

return $out;
avatar
Проще сделать tv типа Selector, в котором задавать список блоков, а выводить сниппетом:
Попробую и так, спасибо.
Комментарий отредактирован 2017-02-20 14:37:22 пользователем Aharito
avatar
Ошибки на самой «вызываемой» странице нет. Я пока тестирую всего одну «страницу-блок» (назвал её так для ясности).

Корректность самой страницы-блока проверяю просто — открываю её на редактирование и нажимаю в админке кнопку «Просмотр».

Вижу, что ошибок нет: в режиме «просмотр кода страницы» именно тот код, что нужен — чистый код блока, безо всяких там DOCTYPE, head и т.д.

URL также возвращается корректный. Странно…
Комментарий отредактирован 2017-02-20 13:51:53 пользователем Aharito
avatar
Ну вы же понимаете, что функцию file_get_contents придумал не я, а разработчики php :) Потому, если у вас ошибка, когда вы пытаетесь получить с ее помощью содержимое страницы с правильным url — то где-то эта ошибка все-таки есть либо у вас на сайте либо в настройках сервера. Возможно, она просто запрещена на уровне php.ini
allow_url_fopen=0
— тогда попробовать использовать curl. Либо все-таки малозаметный косяк в url — бывает такое, когда не сразу разницу между правильным и «корректным» заметишь :)
avatar
ошибка все-таки есть либо у вас на сайте либо в настройках сервера. Возможно, она просто запрещена на уровне php.ini

Да, скорее всего настройки сервера, буду смотреть их. Но сразу возникает вопрос — а насколько это повредит безопасности? ведь не просто же так запрещают открытие УРЛ в настройках.
avatar
Кстати, я понял, почему не работал у меня сниппет с file_get_contents. Не хватало MODx-овской кавычки в параметре сниппета :)

Во какая глубокая ошибка!!! :) ну зато такой замечательный способ с DLTemplate выработался, у меня он уже действует. Всё, что ни делается…
avatar
можно через подмену шаблона DocLister'a
в сниппете prepare

<?php
$id=$data['template'];
$tpl = null;
if ($id > 0) {
	$tpl = $this->db->getValue("SELECT `content` FROM {$modx->getFullTableName("site_templates")} WHERE `id` = '{$id}'");
}
if (is_null($tpl)) {
	$tpl = '[*content*]';
}
$_DocLister->renderTPL = trim($tpl, '{}');
return $data;
avatar
У меня в том документе, который является как бы одним из блоков для формирования лендинга, расположен вызов SimpleGallery. И все, нет ни хедера, ни футера в шаблоне, только нужный кусок HTML фрмируется (вроде бы).

Так формируется выходной HTML этого документа, который мне по идее и шаблонизировать-то не надо, а надо просто взять его и впихнуть в лендинг.

Типа как это на JS делается, только на JS не хочется.

И то же самое с другими документами-блоками сделать надо.

У вас вроде бы несколько другое — или я чего-то не понял?
Комментарий отредактирован 2017-02-20 12:21:57 пользователем Aharito
avatar
идея как у топикстартера:
есть набор документов-блоков со своими шаблонами (в них только секции лэндинга)
всё это собирается доклистером, в сниппете prepare просто подменяется шаблон на шаблон текущего документа

как-то так :)

и да, все вызовы сниппетов в шаблонах-секциях отрабатывают как положено, плюс можно сортировать блоки в дереве ресурсов
avatar
Спасибо. Тут в комментах выше примерно такое же направление подсказали, только без prepare, но суть в общем та же.
avatar
Несколько раз подобное делал. Тут и тут
return $modx->runSnippet('DocLister', array(
        'idType' => 'parents',
        'orderBy' => 'menuindex ASC',
        'addWhereList' => 'c.hidemenu = 0',
        'prepare' => function($data, $modx, $_DL, $_eDL){
                //Так
                $_DL->renderTPL = '@RENDERPAGE: '.$data['id'];
                //Ну или так
                $_DL->renderTPL = '@CODE: '.DLTemplate::getInstance($modx)->renderDoc($data['id'], true);
                return $data;
        }
));


Если не нравится идея подмены шаблона, то всегда можно получить коллекцию документов и там ее отрендерить. В итоге тот же самый сниппет без всяких костылей с подменой шаблона принимает вид
$modx->runSnippet('DocLister', array(
        'idType' => 'parents',
        'orderBy' => 'menuindex ASC',
        'addWhereList' => 'c.hidemenu = 0',
        'saveDLObject' => '_DL'
));
return $modx->getPlaceholder('_DL')->docsCollection()->reduce(function($out, $data) use ($_DL, $modx){
        $tpl = DLTemplate::getInstance($modx);
    return $out.$tpl->renderDoc($data['id'], true);
});

Правда понять суть происходящего сложно без детального изучения DL.
Комментарий отредактирован 2017-02-20 17:37:42 пользователем Agel_Nash
avatar
Правда понять суть происходящего сложно без детального изучения DL.
Несмотря на лень, это приходится постепенно делать. Без DL половина возможностей MODx теряется.

Даже сегодняшнее обсуждение, касающееся, казалось бы, узкого вопроса лендинга, открывает широкие перспективы (если помыслить). Например, столь модные ныне конструкторы лендингов можно реализовать, используя эти методы набора из блоков.

По своему изначальному вопросу: я пока остановился на простом способе «ручного» набора блоков, но сначала хотел делать как в вашем последнем примере, и так и придется делать в некоторых случаях.
Комментарий отредактирован 2017-02-20 18:20:40 пользователем Aharito
avatar
Спасибо, несколько раз пользовался Вашим решением (вариант 2), но столкнулся с проблемой чужих плейсхолдеров, например, есть на странице вывод нескольких последних отзывов, код выглядит так
[!JotX? &docids=`23` &action=`comments` &config=`home` &placeholders=`1` &output=`0` &numdir=`0` &limit=`5`!]
 <div id="reviews" class="reviews">
      <div id="review-carousel" class="review-carousel owl-carousel">
[+jot.html.comments+]
       </div>
  </div>

И ничего не выводится.
Второй безуспешный вариант — пагинация фотогалереи, код

[!sgLister? &parents=`[*id*]` &thumbSnippet=`phpthumb` &thumbOptions=`w=340,h=255,zc=1,bg=ffffff` &display=`18` &orderBy=`sg_index DESC` &id=`cat` &paginate=`pages` &pageLimit=`1` &pageAdjacents=`1` &tpl=`@CODE:
						<div class="col-md-4">
                            <div class="project-item wow animated fadeInUp" data-wow-delay="0.[+sg_index+]s" data-wow-duration="1.[+sg_index+]s">
                                <div class="project-img"><a href="[+sg_image+]" data-gal="prettyPhoto[projects]"><img src="[+thumb.sg_image+]" alt="[+e.sg_title+]"></a></div>
                                <h4 class="project-name">[+e.sg_title+]</h4>
                                <span class="project-size">[+e.sg_description+] м<sup>2</sup></span>
                                <a class="btn btn-block btn-get-plan button--data" href="#" data-toggle="modal" data-target="#modal-price" data-title="[+e.sg_title+]" data-button="Узнать подробнее">Узнайте подробнее</a>
                            </div>
                        </div>`
&TplPrevP=`@CODE: <li><a href="[+link+]" class="page">назад</a></li>`
&TplPage=`@CODE: <li><a href="[+link+]" class="page">[+num+]</a></li>`
&TplCurrentPage=`@CODE: <li  class="active"><a>[+num+]</a></li>`
&TplNextP=`@CODE: <li><a href="[+link+]" class="page">далее</a></li>`
&TplDotsPage=`@CODE:<li><a href="[+link+]" class="page"> ... </a></li>`
&TplWrapPaginate=`@CODE: <ul class="paginate pagination">[+wrap+]</ul>`
!]
[+cat.pages+]

И пагинация не выводится.

Подскажите, как решить эти вопросы?
Спасибо.
avatar
Ничесе, да тут активность есть. Мы для такого переехали на pagebuilder
avatar
А то! Все очень просто и самое главное — логически понятное для админа, даже мануалов никаких не надо)).

Но за наводку на pagebuilder спасибо, обязательно выберу время посидеть над ним.
avatar
Делал такую штуку. Связывал в самописном сниппета id шаблонов с чанками. Каждому типу полосы лендинга соответствовал свой шаблон (со своими полями). Ну и каждый документ получался полосой лендинка

/*
Сниппет aaLPage
выводим блоки
*/
//соответствие между шаблоном и чанком
$configArr = array(
	20 => 'topSlider.LayerSlider.Blck',  //Блок. Слайдер
	61 => 'dcatalog.Blck',               //Блок. Каталог
        65 => 'BigTitleCenter.Blck',         //Блок. Большой заголовок по центру
);

$formId 	= '';
$formIdArr 	= array();
$output 	= '';
//получаем id всех опубликованных документов родителя
if(isset($start)){
	$docArr = $modx->getDocumentChildren($start, 1, 0, 'id,template');
	if(count($docArr) > 0){
		foreach($docArr as $oneDoc){
			$chankName = $configArr[$oneDoc['template']];
			$formIdArr = $modx->getDocumentChildren($oneDoc['id'], 1, 0, 'id', '`template` = 22');
			if(count($formIdArr) > 0){
				$formId = $formIdArr[0]['id'];
			}
			$output = $output.$modx->runSnippet('parseChunk', array('ChunkName'=>$chankName, 
																	'docid'=>$oneDoc['id'], 
																	'formid'=>$formId));
		}
	}
}
echo $output;
Комментарий отредактирован 2017-02-21 12:24:58 пользователем zloyxrom
avatar
Тоже решение, но здесь у вас требуются чанки (насколько я понял — чанки на странице самого лендинга), а в том решении, что выработалось здесь в обсуждении — нужны только шаблоны для документов-блоков, да и то не всегда.

В моем случае достаточно полей документа (pagetitle, longtitle) и SimpleGallery, которая не требует отдельного ТВ и может быть привязана к отдельному документу.

Поэтому даже шаблон у меня один на всех :)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.