Всех приветствую, дорогие друзья! Сегодня мы с вами сделаем конструктор страниц для нашего MODX Revolution на основе бесплатного компонента MIGX. Данный способ реализации текстовой страницы я еще привык называть “правильным”, так как заказчик сам решает какой блок в какое место расположить и чем его наполнить.
Для начала определимся со стандартным набором блоков для нашего конструктора, посмотреть его вы сможете по ссылке https://dev3.dart.agency/oplata.html. Все блоки, которые там представлены вы можете использовать как душе угодно и формировать даже страницы – лендинги (собственно этот способ реализации, так и придумался).
Итак, начнем с первого экрана. Как правило, на страницах-лендингах содержится крупный баннер и оффер с формой. Так как нам не нужны будут хлебные крошки при использовании данного экрана, мы вынесем его отдельно от наших блоков. Для него мы создадим два TV-параметра: slider_firstblock (MIGX-слайдер, пример реализации можно посмотреть здесь) и firstblock (чекбокс отображать наш первый блок или нет). Подробную реализацию смотрите в видео к данной статье.
У меня slider_firstblock получился следующий. Вкладки формы:
[{"caption":"Слайд", "fields": [ {"field":"header","caption":"Заголовок"}, {"field":"alt","caption":"ALT атрибут"}, {"field":"image","caption":"Изображение","inputTVtype":"image","description":"изображение не менее 1920x395"}, {"field":"resid","caption":"ID ресурса для перехода"} ] }]
Разметка колонок:
[{ "header": "Заголовок", "sortable": "true", "dataIndex": "header" },{ "header": "ALT атрибут", "sortable": "true", "dataIndex": "alt" },{ "header": "Изображение", "sortable": "false", "dataIndex": "image","renderer": "this.renderImage" },{ "header": "ID ресурса для перехода", "sortable": "true", "dataIndex": "resid" }]
Теперь, мы можем приступить к нашим блокам. Для начала создадим категорию “Блоки” и запомним ее идентификатор. У меня он получился – 41.
И перейдем к самому интересному, реализации конструктора страниц. Первым создадим TV-параметр block_types с типом ввода “Список с одиночным выбором”, а в поле “Возможные значения” укажем:
@SELECT `description`,`name` FROM `[[+PREFIX]]site_htmlsnippets` WHERE `category` = 41
Где 41 – идентификатор нашей категории “Блоки”. Параметры вывода должны стоять “По умолчанию” и шаблоны отмечать не нужно.
Конечно, нужно понимать, что в каждом нашем блоке есть подблоки, поэтому создадим TV-параметр blockitems с типом ввода MIGX. Вкладки формы:
[{"caption":"Элемент", "fields": [ {"field":"title","caption":"Заголовок"}, {"field":"descriptions","caption":"Текст под заголовком"}, {"field":"icon","caption":"Иконка"}, {"field":"image","caption":"Изображение","inputTVtype":"image"}, {"field":"text","caption":"Текст","inputTVtype":"richtext"}, {"field":"video","caption":"Видео"}, {"field":"href","caption":"Ссылка"} ] }]
Разметка колонок:
[{ "header": "Заголовок", "sortable": "true", "dataIndex": "title" }]
Обратите внимание на поле “descriptions”, оно названо так не случайно. Слово “description” зарезервировано.
Следующий наш шаг будет создание основного рабочего TV-параметра blocks c типом ввода “MIGX”. Вкладки формы:
[{"caption":"Блок", "fields": [ {"field":"title","caption":"Заголовок"}, {"field":"descriptions","caption":"Текст под заголовком"}, {"field":"chunk","caption":"Тип блока","inputTV":"block_types"}, {"field":"class","caption":"Класс"}, {"field":"image","caption":"Изображение","inputTVtype":"image"}, {"field":"video","caption":"Видео"}, {"field":"gallery","caption":"Галерея","description":"Идентификатор галереи"}, {"field":"text","caption":"Текст","inputTVtype":"richtext"}, {"field":"elements","caption":"Элементы","inputTV":"blockitems"} ] }]
Разметка колонок:
[{ "header": "Заголовок", "sortable": "true", "dataIndex": "title" },{ "header": "Чанк", "sortable": "true", "dataIndex": "chunk" }]
Обязательно отметьте здесь соответствующие рабочие шаблоны. Если вы посмотрите на данное TV-поле на странице ресурса, то увидите, что по сути у нас получился MIGX в MIGX’се. При заполнении, главное, не запутаться в таблицах.
Следующим шагом создадим два снипета, которые помогут нам при выводе наших блоков. Непосредственно сниппет по работе с MIGX get_sections.php (я работаю с файлами, вы можете создать в панели управления):
<?php if(!$docid){ $resource = $modx->resource; }else{ $resource = $modx->getObject("modResource", $docid); } if($tvname){ $pdo = $modx->getService('pdoFetch'); $output = ''; $tvcontent = json_decode($resource->getTVValue($tvname), 1); foreach($tvcontent as $section){ $output .= $pdo->getChunk($section["chunk"], $section); } echo $output; }else{ echo "ERROR: не указан TV!"; }
Снипет thumb_youtube.php, который будет брать миниатюры к видео с Youtube:
<?php $url = $modx->getOption('url', $scriptProperties, false); if($url){ $param = parse_url($url); parse_str(parse_url($url, PHP_URL_QUERY), $parameters); return 'https://img.youtube.com/vi/'.$parameters[v].'/maxresdefault.jpg'; }else{ return 'thumbYouTube snippet Error'; }
Класс! Теперь создадим чанки блоков (они обязательно должны быть в панели управления MODX). Конечно, у вас это могут быть другие чанки. Я же приведу примеры своих, основанных на Bootstrap 4. У всех чанков должна быть категория “Блоки”, которую мы с вами создавали в начале, и обязательно должно быть заполнено поле “описание”.
Простой чанк (имя: simpleBlockTpl, описание: Простой блок на всю ширину):
<section class="def-section {$class}" id="section_{$MIGX_id}"> <div class="container"> <h2>{$title}</h2> {if $subtitle} <div class="desc">{$subtitle}</div> {/if} {if $text} <div>{$text}</div> {/if} </div> </section>
Видеогалерея (имя: videoBlockTpl, описание: Блок видеогалереи):
<section class="def-section video {$class}" id="section_{$MIGX_id}"> <div class="container"> <h2>{$title}</h2> {if $subtitle} <div class="desc">{$subtitle}</div> {/if} <div class="video-gallery"> <div class="row"> {if $elements} {set $rows = json_decode($elements, true)} {foreach $rows as $row} <!-- item --> <div class="col-sm-6 col-md-4"> <div class="item"> <a data-fancybox href="{$row.video}"> <img src="{$_modx->runSnippet("@FILE snippets/thumb_youtube.php", ['url' => $row.video])}" alt="{$row.title | htmlent}"> </a> </div> </div> <!-- / item --> {/foreach} {/if} </div> </div> {if $text} <div class="desc">{$text}</div> {/if} </div> </section>
Блок с кругляшами (имя: roundedBlockTpl, описание: Блок с кругляшами):
<section class="def-section for_who {$class}" id="section_{$MIGX_id}"> <div class="container"> <h2>{$title}</h2> {if $subtitle} <div class="desc">{$subtitle}</div> {/if} <div class="interiers"> <div class="row justify-content-center"> {if $elements} {set $rows = json_decode($elements, true)} {foreach $rows as $row} <!-- item --> <div class="col-lg-2 col-md-4 col-6"> <div class="item"> <div class="icon"> {set $image = $_modx->runSnippet("phpthumbon",[ "input" => "assets/files/"~$row.image, "options" => "w=200&h=200&zc=1&q=99" ])} <div class="img" style="background: url({$image}) no-repeat;"></div> </div> <div class="text"> <span class="title">{$row.title}</span> </div> </div> </div> <!-- / item --> {/foreach} {/if} </div> </div> {if $text} <div class="desc">{$text}</div> {/if} </div> </section>
Блок в две колонки (имя: simpleHalfBlockTpl, описание: Блок в две колонки):
<section class="def-section {$class}" id="section_{$MIGX_id}"> <div class="container"> {if $title} <h2>{$title}</h2> {/if} {if $subtitle} <div class="desc">{$subtitle}</div> {/if} <div class="simple-half-block"> <div class="row"> {if $elements} {set $rows = json_decode($elements, true)} {foreach $rows as $row} <!-- item --> <div class="col-md-6"> {$row.text} {if $row.image} <img src="assets/files/{$row.image}" alt="{$row.title}"> {/if} </div> <!-- / item --> {/foreach} {/if} </div> </div> {if $text} <div class="desc">{$text}</div> {/if} </div> </section>
Блок отзывов (имя: reviewBlockTpl, описание: Блок отзывов):
<!-- SECTION REVIEWs --> <section class="def-section reviews {$class}" id="section_{$MIGX_id}"> <div class="container"> <h2>{$title}</h2> {if $subtitle} <div class="desc">{$subtitle}</div> {/if} <div class="items"> <div class="row"> {if $elements} {set $rows = json_decode($elements, true)} {foreach $rows as $row} <!-- item --> <div class="col-md-4 col-sm-6"> <div class="item"> <div class="image"> {set $image = $_modx->runSnippet("phpthumbon",[ "input" => "assets/files/"~$row.image, "options" => "w=297&h=297&zc=1&q=99" ])} <img src="{$image}" alt="{$row.title}"> </div> <div class="text"> <span class="name">{$row.title}</span> {$row.text} </div> </div> </div> <!-- / item --> {/foreach} {/if} </div> </div> {if $text} <div class="desc">{$text}</div> {/if} </div> </section> <!-- / SECTION REVIEWs -->
Блок формы (имя: formBlockTpl, описание: Форма на черном фоне):
<!-- FORM BLOCK --> <div class="form-block" id="section_{$MIGX_id}"> <div class="container"> {$_modx->runSnippet("!AjaxForm", [ 'snippet' => 'FormIt', 'form' => '@FILE chunks/form_text.tpl', 'hooks' => 'spam,email,FormItSaveForm', 'emailTpl' => '@FILE chunks/email_email.tpl', 'emailSubject' => 'Заявка с внутренних блоков', 'emailTo' => $_modx->getPlaceholder('+conf_to_email'), 'emailFrom' => $_modx->config.emailsender, 'formName' => 'Заявка с внутренних блоков', 'validate' => 'page:required,name:required,phone:required,username:blank', 'anchor' => $anchor ])} </div> </div> <!-- / FORM BLOCK -->
Блок аккордеон (имя: faqBlockTpl, описание: Блок аккордеон):
<!-- SECTION REVIEWs --> <section class="def-section faq {$class}" id="section_{$MIGX_id}"> <div class="container"> <h2>{$title}</h2> {if $subtitle} <div class="desc">{$subtitle}</div> {/if} <div id="accordion" class="accordeon"> {if $elements} {set $rows = json_decode($elements, true)} {foreach $rows as $row index=$index} <div class="card"> <div class="card-header" id="heading{$index}"> <h5 class="mb-0"> <button class="btn btn-link" data-toggle="collapse" data-target="#collapse{$index}" aria-expanded="true" aria-controls="collapse{$index}"> {$row.title} </button> </h5> </div> <div id="collapse{$index}" class="collapse {if $index==0}show{/if}" aria-labelledby="heading{$index}" data-parent="#accordion"> <div class="card-body"> {$row.text} </div> </div> </div> {/foreach} {/if} </div> {if $text} <div class="desc">{$text}</div> {/if} </div> </section> <!-- / SECTION REVIEWs -->
Нам осталось вызвать все это дело корректно в шаблоне текстовой страницы:
{extends 'file:templates/index.tpl'} {block 'content'} {if $_modx->resource.firstblock == 1} <div class="title-block mb-5"> <div class="main-slider owl-carousel"> {set $rows = json_decode($_modx->resource.slider_firstblock, true)} {foreach $rows as $key => $row} {set $image = $_modx->runSnippet("phpthumbon", [ "input" => "assets/files/"~$row.image, "options" => "w=1920&h=800&zc=1&q=99" ])} <div class="item" style="background: url({$image}) no-repeat;"> </div> {/foreach} </div> <div class="caption"> <div class="container"> <h1>{$_modx->resource.longtitle ?: $_modx->resource.pagetitle}</h1> {$_modx->runSnippet("!AjaxForm", [ 'snippet' => 'FormIt', 'form' => '@FILE chunks/form_screen.tpl', 'hooks' => 'spam,email,FormItSaveForm', 'emailTpl' => '@FILE chunks/email_email.tpl', 'emailSubject' => 'Заявка с первого экрана', 'emailTo' => $_modx->getPlaceholder('+conf_to_email'), 'emailFrom' => $_modx->config.emailsender, 'formName' => 'Заявка с первого экрана', 'validate' => 'page:required,name:required,phone:required,username:blank', ])} </div> </div> <div class="bouncher"> <a href="" class="bouncing-icon">'</a> </div> </div> {else} {include 'file:chunks/base_breadcrumbs.tpl'} {/if} {$_modx->runSnippet("@FILE snippets/get_sections.php", [ "tvname" => "blocks" ])} {/block}
Вот таким простым образом у нас получился конструктор страниц, который можно расширять. Самое главное он бесплатный и, по-моему, является хорошей альтернативой ContentBlocks.