Всех приветствую, дорогие друзья! Сегодня мы с вами сделаем конструктор страниц для нашего 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.