[СДЕЛАЙ САМ] Конфигуратор страниц с помощью MIGX

Приветствую, дальше будет описание эволюционировавшего метода из моей же более старой заметки, я пытался её обновлять, но получилась каша, поэтому было принято решение написать новую заметку. Кроме того, сама методика сильно спрогрессировала, на мой взгляд, в ней, определенно, стало меньше ручного труда.

Задача: дать возможность контент-менеджеру создавать и изменять страницы используя готовые блоки из вёрстки, а так же быстрее интегрировать вёрстку.

Нам понадобится:
1. MIGX установленный на сайт.
2. Console установленная на сайт.
3. Вёрстка, которая не ломается при изменении порядка блоков на странице.
4. Несколько базовых конфигураций.

Подготовка вёрстки
Файлы вёрстки могут лежать где угодно, но так, чтобы к ней можно было получить доступ из Console. Я делаю копию в папку core/elements/layout/ для простоты доступа и чтобы оригинал сохранился, на всякий пожарный. Когда вёрстка попала в нужное место, нужно выполнить интеграцию, т.е. расставить переменные Fenom на места тех элементов которые должны редактироваться из админки. Для наглядности приведу пример.
<!--**Контакты**-->
    <!--*#parent#*-->
    <!--##contacts_block##-->
    {* контакты *}
    {set $contacts = $parent | get_contacts: 'contacts'}
    
    {* метрики *}
    {set $ym_id = $_modx->config.ym_id}
    
    {* формы *}
    {set $secret = $_modx->config.secret}
    {set $email_from = 'noreply@'~$_modx->config.http_host}
    {set $validateDefault = 'secret:contains=^'~$secret~'^'}
    {set $static_blocks_page_id = $_modx->config.static_blocks_page_id}
    {set $form_list = $static_blocks_page_id | resource: 'form_list' | fromJSON}
    
    {* поля *}
    {set $longtitle = $parent | resource: 'longtitle'}
    {set $introtext = $parent | resource: 'introtext'}
    {set $map = $parent | resource: 'map'}
    <section class="contact-section spad">
        <div class="container">
            <div class="row">
                <div class="col-lg-4">
                    <div class="contact-text">
                        <h2>{$longtitle}</h2>
                        {if $introtext}
                        <p>{$introtext}</p>
                        {/if}
                        <table>
                            <tbody>
                            {if $contacts.addresses}
                            {foreach $contacts.addresses as $item}
                            <tr>
                                <td class="c-o">{$item.caption}</td>
                                <td><a href="tel:{$item.value}">{$item.icon_class} {$item.value}</a></td>
                            </tr>
                            {/foreach}
                            {/if}

                            {if $contacts.phones}
                            {foreach $contacts.phones as $item}
                            <tr>
                                <td class="c-o">{$item.caption}</td>
                                <td><a href="tel:{$item.value}">{$item.icon_class} {$item.formattedValue}</a></td>
                            </tr>
                            {/foreach}
                            {/if}

                            {if $contacts.emails}
                            {foreach $contacts.emails as $item}
                            <tr>
                                <td class="c-o">{$item.caption}</td>
                                <td><a href="mailto:{$item.value}">{$item.icon_class} {$item.value}</a></td>
                            </tr>
                            {/foreach}
                            {/if}

                            {if $contacts.socials}
                            <tr>
                                <td class="c-o">Соц.сети</td>
                                <td>
                                    {foreach $contacts.socials as $item}
                                    <a href="{$item.value}">{$item.icon_class}</a>
                                    {/foreach}
                                </td>
                            </tr>
                            {/if}
                            </tbody>
                        </table>
                    </div>
                </div>
                {if $form_list}
                <div class="col-lg-7 offset-lg-1">
                    {foreach $form_list as $form}
                    {if $form.fid == 'contactForm'}
                    ##'!AjaxForm' | snippet : [
                    'form' =>  '@FILE chunks/forms/{$form.fid}.html',
                    'snippet' => 'FormIt',
                    'hooks' => 'FormItSaveForm,email',
                    'emailSubject' => '{$form.name}',
                    'emailTo' => '{$form.emails}',
                    'emailFrom' => '{$email_from}',
                    'emailTpl' => '@FILE chunks/emails/tplFormEmail.html',
                    'validate' => '{$form.validate~$validateDefault}',
                    'validationErrorMessage' => 'Исправьте, пожалуйста, ошибки!',
                    'successMessage' => '{$form.success_msg}',

                    'btnText' => '{$form.btn_text}',
                    'formName' => '{$form.name}',
                    'ymGoal' => '{$form.ym_goal}',
                    'ymId' => '{$ym_id}'

                    'phone.vTextRequired' => 'Укажите телефон.',
                    'name.vTextRequired' => 'Укажите имя.',
                    'phone.vTextMinLength' => 'Слишком короткий телефон.',
                    'name.vTextMinLength' => 'Слишком короткое имя.',
                    'organization.vTextRequired' => 'Напишите название организации.',
                    'organization.vTextMinLength' => 'Слишком короткое название.',
                    'email.vTextRequired' => 'Укажите электронную почту.',
                    'email.vTextEmail' => 'Это не электронная почта.',
                    'message.vTextRequired' => 'Напишите хоть пару строк.',
                    'message.vTextMinLength' => 'Суть не раскрыта.',
                    'secret.vTextContains' => 'Кажется Вы робот. Если это не так, обновите страницу.'
                    ]}
                    {/if}
                    {/foreach}
                </div>
                {/if}
            </div>
            {if $map}
            <div class="map">
                {$map}
            </div>
            {/if}
        </div>
    </section>
    <!--##contacts_block-->
В блоке «НАМ ПОНАДОБИТСЯ» есть ссылка на архив с дампом таблиц с моими базовыми конфигурациями, если уже какие-то конфигурации на сайте есть, импорт моего дампа их, скорее всего, затрёт. Предполагается, что вы делаете сайт с нуля.

Давайте расскажу, что к чему в примере интегрированной вёрстки.
Первое на что следует обратить внимание на комментарии html «звёздочки» и «решётки» в них это не для красоты, а для упрощения регулярных выражений.

<!--** **--> - в таком комментарии указывается название секции на понятном для пользователя языке, желательно писать что-то понятное и отражающее содержимое секции.

<!--*# #*-->  - список полей через запятую, в названиях самих полей не рекомендую использовать ничего кроме латинских букв и нижнего подчёркивания, не используйте пробелы тут.

<!--## ##--> - название секции оно же используется как часть имени файла шаблона этой секции, не используйте пробелы тут.

<!--## ##--> код шаблона секции <!--## -->.

Переменные, которые неопределенны через set непосредственно в шаблоне, передаются в него из плагина, до него ещё доберёмся. Обратите внимание, что есть переменная $static_blocks_page_id, она содержит ID ресурса, в котором можно будет редактировать статичные блоки. Статичными блоками я называю такие части вёрстки которые могут быть добавлены на разные страницы сайта, но при этом всегда имеют одинаковое наполнение. Такие блоки логично редактировать в одном месте и для этого я создаю отдельный непубликуемый ресурс с отдельным шаблоном, а его ID записываю в ClientConfig.
!!! ВАЖНО!!! Плагин, который будет частично парсить конфигурации, будет пересохранять все ресурсы, которые используют текущий ресурс. Например, есть статичный блок, вы его хотите выводить на всех страницах, это означает, что все страницы будут использовать ресурс со статичными блоками, о котором я рассказывал. Соответственно, при внесении изменений в ресурс «Статичные блоки», все остальные страницы будут пересохранены, а конфигурации перегенерированы. Я не проверял, но есть подозрение, что если ресурсов много, например товаров, то при внесении изменений в ресурс «Статичные блоки» админка может зависнуть или вовсе отвалиться, поэтому будьте осторожнее, либо отключите перегенерацию зависимых конфигураций в плагине.
UPD 09.07.2022 Я решил отказаться от порочной практики пересохранения ресурсов, а чтобы упростить наполнение создаю технические страницы, которые соответствуют верстке, т.е. я их наполняю и создаю отдельный шаблон под каждый тип страниц (идею украл у @Александр Мельник надеюсь он меня простит). Не надо пугаться, шаблон я создаю только в админке, физически все они ссылаются на 1 файл. К этому добавлен ещё один плагин, который копирует конфигурацию в создаваемый ресурс из соответствующего технического ресурса. Тут есть нюанс, я не нашел события смены шаблона, поэтому, чтобы плагин сработал, нужно у нового ресурса выбрать шаблон, сохранить, а потом ещё раз сохранить. Костыль, конечно, так что если кто-то подскажет вариант лучше, буду признателен.

Далее вас, вероятно, заинтересует странный вызов сниппета AjaxForm. Он начинается с ## не просто так. Дело в том, что некоторые сниппеты отдают статичные данные и их можно получить при обработке конфигурации в плагине, в этом случае вызывать сниппет надо как обычно. Однако, если сниппет должен быть вызван некешированным (AjaxForm, pdoPage, mSearch2 и т.п.), то парсить его плагином нельзя, для этого { в вызове нужно заменить на ##, а плагин вернёт всё как должно быть и сниппет будет вызван при отдаче страницы на фронт. И так нужно записывать все вызовы (плейсхолдеры, сниппеты и т.д.), обычный синтаксис следует использовать только для элементов, которые содержаться непосредственно в секции.

Скрипт для Console
Теперь можно в Console запустить скрипт, который создаст секции в MIGX и создаст файлы шаблонов этих секций. В скрипте нужно указать пути к вёрстке и к папке для сохранения шаблонов секций.
<?php
function deleteUndueFields($defaultFields,$needFields){
    $fields = array();
    if(!$needFields){
        return $fields;
    }
    $needFields = explode(',', $needFields);
    
    foreach($defaultFields as $k => $v){
        if(in_array($v['field'],$needFields)){
            $fields[] = $v;
        }
    }
    return $fields;
}

$modx->addPackage('migx', $modx->getOption('core_path').'components/migx/model/'); // подключаем модель объекта MIGX

$defaultConfig = $modx->getObject('migxConfig', array('name' => 'base')); // получаем базовую конфигурацию
$defaultConfig = $defaultConfig->toArray(); // преобразуем в массив
unset($defaultConfig['id']); // удаляем id, т.к. он задается автоматически при создании объекта
$defaultFormTabs = json_decode($defaultConfig['formtabs'],1); // базовая вкладка формы
$defaultFields = $defaultFormTabs[1]['fields']; // базовые поля
unset($defaultFormTabs[1]['fields']); // удаляем базовые поля из базовой вкладки формы
$sectionsPath = MODX_BASE_PATH . 'core/elements/sections/'; // путь для сохранения шаблонов секций
$layoutPath = MODX_BASE_PATH . 'core/elements/layout/'; // путь к вёрстке
$fileNames = scandir($layoutPath); // собираем все имена файлов в массив
unset($fileNames[0],$fileNames[1]); // удаляем ненужные нам элементы массива, которые возвращает функция scandir()

foreach($fileNames as $name){
    $fileData = file_get_contents($layoutPath.$name);
    preg_match_all('/<!--\*\*(.*?)\*\*-->/',$fileData,$captions);
    preg_match_all('/<!--\*#(.*?)#\*-->/',$fileData,$fields);
    preg_match_all('/<!--##(.*?)##-->/',$fileData,$tplnames);
    preg_match_all('/##-->(.*?)<!--##/s',$fileData,$tpls);
     
    foreach($tplnames[1] as $key => $value){
        $file_name = $value.'.tpl';
        $file_name_vis = 'core/elements/sections/' . $file_name;
        $defaultFormTabs[1]['fields'] = deleteUndueFields($defaultFields,$fields[1][$key]);
        $defaultFormTabs[0]['fields'][2]['default'] = $file_name_vis; // устанавливаем имя файла секции
        $defaultFormTabs[0]['fields'][2]['useDefaultIfEmpty'] = 1;
        $defaultFormTabs[0]['fields'][1]['default'] = $captions[1][$key]; // устанавливаем имя секции
        $defaultFormTabs[0]['fields'][1]['useDefaultIfEmpty'] = 1;
        $defaultFormTabs[0]['fields'][0]['default'] = $value; // устанавливаем id секции
        $defaultFormTabs[0]['fields'][0]['useDefaultIfEmpty'] = 1;
        
        $defaultConfig['formtabs'] = json_encode($defaultFormTabs);
        $defaultConfig['name'] = $value;
        $defaultConfig['extended']['multiple_formtabs_optionstext'] = $captions[1][$key];
        $defaultConfig['editedon'] = date('Y-m-d H:i:s');
        
        if(!$config = $modx->getObject('migxConfig', array('name' => $value))){
            $config = $modx->newObject('migxConfig');
        }
        
        $config->fromArray($defaultConfig);
        $config->save();
       
        if(!file_exists( $sectionsPath.$file_name) && $tpls[1][$key]){
            file_put_contents($sectionsPath.$file_name, $tpls[1][$key]);
        }
    }
 }
Основной плагин
<?php
// VARIABLES
$pathToParsedConfigs = MODX_CORE_PATH.'elements/parsed/configs/'.$id.'.tpl';
$pdoTools = $modx->getService('pdoTools');
$ctx = $resource->get('context_key');
$sbp_id = $modx->getOption('static_block_page_id', null, '');

// FUNCTIONS
if(!function_exists('prepareToParseMIGXConfig')){
    function prepareToParseMIGXConfig($modx,$resource,$pdoTools,$sbp_id, $ctx = 'web'){
        if($config = $resource->getTVValue('config')){ // если в ресурсе есть поле с конфигурацией
            $rid = $resource->get('id');
            $chunkPath = '@FILE sections/';
            $pathToDist =  MODX_CORE_PATH.'elements/parsed/configs/';
            parseMIGXConfig($modx,$config,$rid, $chunkPath, $pathToDist, $pdoTools, $sbp_id, $ctx); // парсим её и генерируем файл
           
        }else{ // если конфигурации нет
            $pathToFile =  MODX_CORE_PATH.'elements/parsed/configs/' . $rid . '.tpl';
            if(file_exists($pathToFile)){ // проверяем есть ли файл с распарсенной конфигурацией
                unlink($pathToFile); // и удаляем его
            }
        }
    }
}


if(!function_exists('parseMIGXConfig')){
    function parseMIGXConfig($modx,$config,$id, $chunkPath, $pathToDist, $pdoTools, $sbp_id, $ctx = 'web'){
        if(!$pdoTools){
            $pdoTools = $modx->getService('pdoTools');
        }
        $sections = json_decode($config, 1); // декодируем конфиг в массив
        $pathToDist .= $id . '.tpl'; // формируем имя файла
        $html = ''; // готовим переменную для содержимого файла
        $i = 1;
        foreach($sections as $key => $section){
            // пробускаем базовую секцию
            if( $section['MIGX_formname'] == 'base'){
                continue;
            }
            $section['rid'] = $id; // передаем на страницу id текущего ресурса
            $section['idx'] = $i; // передаем на страницу порядковый номер секции
            $section['sbp_id'] = $sbp_id; // передаем на страницу id ресурса со статичными блоками
            foreach($section as $key => $value){
                if(strpos($value, '[{') !== false){
                    $section[$key] = json_decode($value,1); // преобразуем поля типа migx в массив               
                }
            } 
           
            $chunkName = $section['MIGX_formname']; // получаем имя чанка
            $chunk = $chunkPath . strtolower($chunkName).'.tpl'; // получаем путь к чанку   
            $tmp = $pdoTools->parseChunk($chunk, $section); // парсим чанк
            $html .= str_replace('##', '{', $tmp); // чтобы на фронте работал парсер pdoTools
            $i++;
        }
        file_put_contents($pathToDist, $html); // генерируем файл
        return true;
    }
}


// EVENTS
switch ($modx->event->name) {
    case 'OnDocFormDelete':
        // удаляем файл с html
        if(file_exists($pathToParsedConfigs)){
            unlink($pathToParsedConfigs);
        }
    break;
        
    case 'OnResourceUndelete':
    case 'OnDocFormSave':
        prepareToParseMIGXConfig($modx,$resource,$pdoTools,$sbp_id,$ctx); // запускаем процесс обработки ресурсов
    break;    
}
Плагин для копирования конфигурации из технического в новый ресурс
<?php
// EVENTS
$donor_parent = 666;
$config_tv_id = 77;
switch ($modx->event->name) {
    case 'OnDocFormSave':
        if($mode === 'upd'){
            $template = $resource->get('template');
            $parent = $resource->get('parent');
            if($parent != $donor_parent){
                if($template && $modx->getCount('modTemplateVarTemplate', array('tmplvarid' => $config_tv_id, 'templateid' => $template))){
                    if($donor = $modx->getObject('modResource', array('template' => $template, 'parent' => $donor_parent))){
                        if($donorConfig = $donor->getTVValue('config')){
                            if(!($config = $resource->getTVValue('config'))){
                                $resource->setTVValue('config', $donorConfig);
                            }
                        }
                    }
                }
            }
        }
    break;    
}
Чтобы плагин работал как надо, нужно в БД создать таблицу related_resources с полями id, master и slave все поля типа int и длиной 10. id должен быть первичным (PRIMARY) ключом, два других должны быть уникальными (UNIQUE) ключами.!!! ВАЖНО!!! @Павел Бигель сказал, что INSERT IGNORE работает не со всеми версиями MYSQL разрешёнными для Modx Revolution и это может сломать ваш сайт. В этом случае замените вызов, используя ON DUPLICATE KEY UPDATE.
Сами плагины нужно привязать к событиям перечисленным внутри оператора switch.

Заключение
В заключение добавлю, что для больших проектов этот способ нужно применять с осторожностью. А в целом, на мой взгляд, при должной сноровке, такой способ интеграции вёрстки помогает ускорить процесс за счёт автоматизации части действий. А если использовать свою сборку, где плагин, таблица, структура папок и нужные компоненты есть из коробки, то интеграция вёрстки будет происходить гораздо быстрее.
Если у кого-то появятся вопросы или предложения пишите в комментариях.
P.S.Ссылка на видео с конечным результатом.
P.P.S. @Алексей Смирнов выпустил компонент со схожим функционалом, желающие могут попробовать, пока бесплатно.
Артур Шевченко
12 мая 2022, 23:17
modx.pro
3 017
+4
Поблагодарить автора Отправить деньги

Комментарии: 25

Александр Мельник
13 мая 2022, 10:21
0
Круто, но мне кажется, это понятно пока только для вас)
Я вот прочел и не понял, как например менеджер может задать на определенной странице свой уникальный набор блоков и что самое важно — наполнить эти поля данными.
Я просто недавно тоже «изобретал» конструктор блоков на migx, потому что на некоторых сайтах возникает задача — хотим вывести на этой странице вверху блок — наши преимущества и указать один набор преимуществ. А на другой странице внизу вывести этот же блок, на заполненный другими данными. а на третьей странице вообще отключить данный блок.
    Артур Шевченко
    13 мая 2022, 10:54
    +1
    Есть TV config типа migx привязываешь к нужному шаблону и заполняешь. Сначала выбираешь какую секцию(блок) нужно вставить, вносишь данные и сохраняешь. Потом добавляешь следующую секцию(блок). Это если секции должны иметь разное наполнение для разных ресурсов, если одинаковое, то есть отдельный ресурс «Статичные блоки» там по категориям разбиты наборы TV, а в config просто нужно выбрать эту секцию и заполнять ничего не нужно(почти). А порядок секций(блоков) меняется как обычно перетаскиванием.
    Согласен, в письменном виде это сложно воспринимать, запишу ещё пояснительное видео с результатом.
      Александр Мельник
      13 мая 2022, 12:02
      0
      да, текстом сложновато воспринимать такую информацию. за видео — заранее спасибо.
vectorserver
13 мая 2022, 13:25
-1
Ерунда какая-то, это мое мнение! Я быстрее страницу заверстаю!
    Артур Шевченко
    13 мая 2022, 14:15
    0
    Надо уточнить, данный метод даёт возможность контент-менеджеру управлять структурой страниц и нужен он в первую очередь для этого.
      vectorserver
      19 мая 2022, 07:28
      0
      Вот за что минусить!?
      За то что я высказался не так как тебе удобно!?
      Все что ты описал есть в MagicThemes!
        Артур Шевченко
        19 мая 2022, 11:20
        0
        Минус за некоструктивную критику. Мы все поняли какой ты классный верстальщик, но к сути того, что я написал в заметке это не относится.
      Константин Ильин
      19 мая 2022, 11:49
      0
      Реализация возможно не совсем понятная, но сама идея довольно популярная.

      Не так давно делал сайт, там много посадочный страниц и обираются из блоков, менеджеру, сеошнику, достаточно загрузить блоки и страница собрана.
      Тоже через MIGX реализовал.

      Вот пример позиции блоков:
      prnt.sc/buLcskvS_nBr

      Если редактировать запись, внутри каждой есть выпадашка с готовыпи блоками и Кодом блока.
      prnt.sc/dBuQ7WP6N2Qu
      Выбираешь, загружаешь код кнопкой в редактор и уже там можно что-то поменять если оно надо(Например заголовок блока или какой то текст), а так просто нажал сохранить и все — блок готов.
      Алексей Смирнов
      13 мая 2022, 19:23
      +1
      Ерунда какая-то, это мое мнение! Я быстрее страницу заверстаю!
      Ну и не используйте это решение.
      Тем более тут вопрос не в быстроте верстки, а управлении повторяющимися блочками.
      И кстати это самое управление уже много кто так или иначе реализовали. (Контент блоки, MagicThemes, и прочее)
      Теперь, вот, есть решение на MIGX.
        Роман
        13 мая 2022, 21:52
        +1
        Не важно, будет этим кто-то пользоваться или нет. Самое главное, что есть какие-то реальные задачи и их решения. Не так много, кто готов поделиться рабочей схемой, даже самой простой.
          Александр Мельник
          15 мая 2022, 12:05
          +2
          Опишу вкратце, как организовывал подобный функционал я, может кому то будет интересно.
          Кода не будет, будут только общие концепции.
          1) Есть конфигурация migx которая содержит
          — название блока. Это для того чтобы менеджеру было сразу понятно, о чем этот блок. Никакой другой цели не несет.
          — скриншот. Хранит скриншот того, как этот блок выглядит на странице. Тоже чтобы менеджеру было удобно.
          — заголовок блока. Это текст, который может быть в блоке а может и нет. Например на одной странице нам нужно вывести иконки и написать вверху над ними — наши преимущества. А где то нужно обойтись только иконками.
          — чекбокс Активен блок или нет.
          — название чанка, хранящего верстку блока.
          — описание. Для удобства менеджера, если управление блоком не очевидно и требует каких то комментариев.
          — откуда брать данные для блока. Выпадающий список с такими вариантами. — брать с текущей страницы — брать с общих настроек. Детальнее что это опишу ниже.
          2) На основании этой конфигурации создаем ТВ поле блок. Привязываем к нужным нам шаблонам.
          3) Для удобства менеджера облегчим ему жизнь. Создадим несколько служебных ресурсов. Назовем их
          — общие настройки
          — категория товара
          -товар
          — инфостраница
          Называем примерно так, чтобы была визуальна видна связь с шаблонам. Зачем нужны эти ресурсы. Они будут являться образцами. К примеру мы идем в служебный ресурс Товар и создаем там блоки (имеются ввиду ТВ поля типа migx).
          Настраиваем их, располагаем в нужном нам порядке, так как хотим чтобы выглядела страница товара.
          4) При создании любого ресурса срабатывает наш плагин (важно. именно при создании нового а не при обновлении) который понимает какой именно ресурс хотят создать (анализируя шаблон или анализируя родителя, внутри которого его хотят создать). К примеру плагин понял что хотят создать товар. Он идет в служебный ресурс товар и копирует оттуда все блоки, которые заранее настроил менеджер. Если плагину не удалось точно понять, какой ресурс хотят создать, он скопирует блоки с служебного ресурса — общие настройки.
          5) мы сохранили новый ресурс и он уже заполнен блоками в нужном нам порядке. Но если менеджер хочет что то изменить, он может редактировать блоки уже принадлежащие этому ресурсу. Сделать эту страницу чем то отличающуюся от стандартного шаблона.
          6) как это все работает и почему блоки отображаются на странице. А шаблонах не содержится верстки, кроме основного скелета html. На странице вызывается самописный сниппет buildpage задача которого получить значение migx tv с блоками на этой странице, получить инфу активен ли блок (нужно ли его вообще отображать на странице) и если активен — вызвать нужный чанк (имя которого тоже лежит в блоке). Очередность блоков задается менеджером путем перетаскивания строк в админке.
          7) последнее время чанки, которые формируют внешний вид блока начал делать по типу компонентов vue. Имеется ввиду лишь идея, в чанке прописан как блок со стилями, так и html так и javascript относящийся к блоку. Соответственно если блок не активен, то нет и лишних стилей и скриптов.
          8) осталось рассказать только об одном — о данных. Сами блоки — это лишь внешний вид, им нужно откуда то брать данные, причем эти данные могут быть разными для блока, в зависимости от того на какой странице он вызван. Данные для блоков храню так же в migx tv полях. Возьму для примера тот же блок — наши преимущества (не знаю как у вас но наши сеошники аж трясутся над этим блоком и пихают его чуть ли не по 10 штук на страницу). Так вот. Создаю конфигурацию migx задача которой хранить изображения. Создаю ТВ поле — блок наши преимущества, которые построен на этой конфигурации. Это ТВ поле есть как у служебных ресурсов, так и у реальных ресурсов, создаваемых менеджером. Настройка в блоке, с именем «Откуда брать данные» как раз и отвечает за это. Менеджер может заполнить один раз данные для блока преимущества (загрузить иконки, возможно написать текст) в служебном ресурсе Общие настройки. Активируя блок «наши преимущества» на любой странице сайта менеджер просто выбирает — брать данные из общих настроек. Если нужно для этой страницы задать индивидуальные данные, то нужно всего лишь перезаполнить соответствующее ТВ поле уже этого ресурса и выставить у блока — «брать данные с этой страницы».
          В общем то и все.
            Артур Шевченко
            15 мая 2022, 12:26
            0
            Почти как у меня))) И я пожалуй спионерю у тебя пару идей))) Добавлю скриншоты и сделаю заготовки для шаблонов. У тебя твой сниппет который всё это разбирает не тормозит загрузку страницы? В моем варианте происходит прегенерация кода шаблона он сохраняется в файл и потом парсится как обычный шаблон, это здорово, некоторые страницы получаются статичными, но вот обновлять их может оказаться ресурсозатратно. Можешь показать код сниппета?
              Александр Мельник
              15 мая 2022, 12:49
              0
              <?php
              $fqn = $modx->getOption('pdoFetch.class', null, 'pdotools.pdofetch', true);
              $path = $modx->getOption('pdofetch_class_path', null, MODX_CORE_PATH . 'components/pdotools/model/', true);
              if ($pdoClass = $modx->loadClass($fqn, $path, false, true)) {
                  $pdoFetch = new $pdoClass($modx, $scriptProperties);
              } else {
                  return false;
              }
              
              
              $blockList = $modx->resource->getTVValue("blockList");
              $blockList = json_decode($blockList ,true);
              
              $html = "";
              foreach ($blockList as $key => $block){
                  if (empty($block['chunkName']) or $block['isActive'] != 'Да')
                      continue;
              
                  $chunk = '@FILE blocks/'.$block['chunkName'];
              
                  $html .= $pdoFetch->getChunk($chunk, $block);
              }
              
              return $html;
              примерно так.
                Александр Мельник
                15 мая 2022, 12:51
                0
                внутрь чанка пробрасывается информация о блоке. чтобы уже внутри чанка можно было разобраться, откуда грузить данные.
                  Артур Шевченко
                  15 мая 2022, 22:29
                  0
                  Ты так и не сказал, если блоков много время ответа сервера не страдает?
                    Александр Мельник
                    16 мая 2022, 08:50
                    0
                    время ответа сервера не идеально, но не критично.
                    Я использую такую блочную систему только если руководство требует ручного управления блоками на странице. Это нужно далеко не везде, чаще всего макеты страниц статичны.
                Артур Шевченко
                19 июня 2022, 10:12
                0
                Расскажи, пожалуйста подробнее про пункт 4 на какое событие у тебя плагин и что в нём? А то я думаю сделать так же и этот момент смущает, потому что не понимаю как подсунуть данные в форму редактирования из другого ресурса.
                  Александр Мельник
                  20 июня 2022, 07:37
                  0
                  У меня работает следующим образом.
                  Смотрим скриншот.

                  Есть тв поле blocklist которое содержит в себе перечень всех возможных на сайте блоков. Под блоком я понимаю — название, указание на чанк в котором вьюшка и прочая инфа.
                  В ресурсе «Настройки (2)» перед началом работы с сайтом я заполняю это поле, причем заполняю максимально, тоесть все все блоки которые есть на данный момент.
                  Чтобы менеджеру было проще, а система в целом была гибче, есть еще группа ресурсов, на скрине это ресурсы в родителе «Настройка шаблонов (48)». Эти ресурсы уже отвечают за какой-то определенный тип страниц. Здесь я тоже перед началом работы создаю все все блоки, НО ненужные деактивирую, сортирую блоки, так чтобы они соответствовали макету этой страницы. Ну к примеру если это услуга, то я деактивирую все, кроме блоков
                  — хлебные крошки
                  — h1
                  — баннер
                  — текстовое поле
                  — форма заказа
                  — ссылка назад
                  Что происходит далее. Я через инструмент «Настройка форм» и через компонент «Collections» стараюсь максимально автоматизировать правильное применение шаблонов, когда менеджер создает новый ресурс.
                  И вот уже при создании нового ресурса срабатывает плагин, который просто наполняет этот новый ресурс блоками (заполняет тв поле blocklist). Если плагин видит, что создается ресурс с шаблоном 8 (к примеру это услуги) то он скопирует этот список с ресурса 56, где уже настроены блоки для услуг. Если не можем понять, какую именно страницу создает менеджер, то будет скопирован список блоков со страницы «Настройки», тоесть полный список и менеджер сам потом деактивирвует ненужное, расставит блоки в нужной очередности.
                  В итоге получается, что менеджер может:
                  — быстро создать страницу с уже заранее продуманными блоками под нее
                  — после создания изменить страницу как угодно, ведь скопировались абсолютно все блоки, просто они деактивированы. Ничто не мешает конкретно эту страницы «услуги» сделать совершенно другой и активировать на ней блок «слайдер» и блок «дополнительная форма обратной связи».
                  — изменить в какой-то момент настройки (перечень активных блоков) для тех же услуг (если смотреть на скрин то изменить блоки в ресурсе 53) и все создаваемые после этого страницы тоже изменятся.
                  Сам плагин очень примитивен.
                  <?php
                  switch ($modx->event->name) { 
                      case 'OnDocFormSave':
                          $template = $resource->get('template');
                          switch ($template) {
                              case '3': // продукция
                                  $example = 49;
                                  break;
                              case '8': // услуги
                                  $example = 53;
                                  break;
                              case '7': //новость
                                  $example = 56;
                                  break;
                                        
                              case '9': // типовые решения
                                  $example = 55;
                                  break;
                                  
                              case '4': // инфо страница
                                  $example = 57;
                                  break;
                                   
                              case '19': // акции
                                  $example = 62;
                                  break;
                              case '21': // реализованные проекты
                                  $example = 54;
                                  break;
                              default:
                              $example = 2;
                                  break;
                          }
                  
                          if ( $resource->get('id')!=2 and $mode == 'new') {
                             
                              $where = array(
                                  'contentid' => $example
                                , 'tmplvarid' => 3
                              );
                          $tv = $modx->getObject('modTemplateVarResource', $where);
                          $value=  $tv->get('value');
                             
                              $resource->setTVValue('blockList', $value);
                          }
                          
                      break;
                  }
                    Артур Шевченко
                    20 июня 2022, 10:22
                    0
                    Я через инструмент «Настройка форм» и через компонент «Collections» стараюсь максимально автоматизировать правильное применение шаблонов, когда менеджер создает новый ресурс.
                    А можно подробнее?) Какие именно манипуляции позволяют автоматизировать наполнение blocklist?
                      Александр Мельник
                      20 июня 2022, 12:24
                      0
                      я имел ввиду, что использование collections или же «настройки форм» позволяет назначать создаваемым ресурсам корректные шаблоны. Тоесть если создает менеджер ресурс. Кликнула правой кнопкой мыши по Услуги и выбирает — создать ресурс, то при создании ему автоматически будет добавлен шаблон — Услуга. А мой плагин уже ориентируется на этот шаблон и понимает из какого вспомогательного ресурса (те что на скрине внутри 48) ему нужно забрать содержимое blocklist и скопировать его в создаваемый ресурс.
                Алексей Смирнов
                15 мая 2022, 18:06
                0
                Вы сейчас прям описываете работу компонента MagicThemes.
                Где весь описываемый функционал есть и в удобном варианте.
                Чуть по другому работает, но суть такая же.
                  Артур Шевченко
                  15 мая 2022, 22:30
                  0
                  Лёш, чес слово, я найду время потестить твой доп, щас проект доделаю и найду)))
                    Алексей Смирнов
                    15 мая 2022, 23:22
                    0
                    А мне вот тож сдать еще проект и допилить более четкую инструкцию.
                    Потому как в твоем решении я увидел еще один сигнал об удобстве управления контентом.
                    В общем, учитывая кол-во появляющихся «хотелок» то развитие тем, конструкторов очень даже интересная и востребованная тема.
                    Будем ее развивать. Хочется использовать готовое решение в доль и поперек, а не только как сборка-modx (не буду называть сайты которые это предлагают). :)
                      Артур Шевченко
                      16 мая 2022, 00:47
                      0
                      Если нужна будет помощью, обращайся, чем смогу помогу.
                  Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                  25