PageBlocks. Выводим SEO-карточки

Задача: Вывести в ресурсе SEO-карточки из других ресурсов.
Пример:

Создаем вкладку с полями


Для реализации будем использовать PageBlocks в режиме developer.
Файл: /core/App/Models/Resource.php

public static function getTabs(): array
{
    return [
        // Вкладка с именем SeoCard
        Tab::make('SeoCard')
            ->position(0)
            ->fields([
                // Заголовок
                Field::make('seo_title')->label('Title')->required(),
                // Описание
                Field::make('seo_description')
                    ->label('Description')
                    ->type('textarea')
                    ->height(100)
                    ->required(),
                // Изображение
                Field::make('seo_img')->label('Image')->type('image')->required(),
                // Таблица
                Field::make('seo_list')
                    ->label('Benefits')
                    ->type('table')
                    ->fields([
                        Field::make('Title')->required(),
                    ])
                    ->columns([
                        Column::make('Title')
                    ]),
            ]),
    ];
}

Результат:



Выводим на сайт


Для вывода используем сниппет pbResources.

{'!pbResources'|snippet: [
    'parents' => 0,
    'resources' => '1,2,3', // Выводим только определенные ресурсы
    'tables' => 'seo_list as list' // Присоединяем таблицу seo_list и заменяем имя на list
    'tpl' => 'pb-seo-card', // Чанк вывода
]}

Чанк pb-seo-card
<div class="col-4">
    <div class="card">
        <img loading="lazy" src="{$seo_img.url}" width="395" height="450" class="card-img-top" alt="{$seo_img.title}">
        <div class="card-body">
            <h5 class="card-title">{$seo_title}</h5>
            <p class="card-text">{$seo_description}</p>
            <ul class="text-start">
                {foreach $list as $item}
                    <li>{$item.title}</li>
                {/foreach}
            </ul>
        </div>
    </div>
</div>

Готово! Результат можно увидеть на первой картинке.
Aleksandr Huz
14 февраля 2025, 11:31
modx.pro
3
1 274
+12
Поблагодарить автора Отправить деньги

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

Николай Савин
14 февраля 2025, 15:45
0
Саш, а откуда карточки то? Есть реальный сайт?
    Aleksandr Huz
    14 февраля 2025, 15:53
    +1
    Реального сайта нет. Меня попросили показать как вывести из несколько ресурсов seo-карточки, где будет картинка, заголовок, описание и еще таблица.
    Сергей Сергеевич
    14 февраля 2025, 17:28
    0
    А что за сниппет такой pbResources? В доке его нет. Зачем он нужен, если есть pdoResources?
      Aleksandr Huz
      14 февраля 2025, 22:35
      +1
      Сниппет pbResources — это аналог pdoResources, но с возможностью присоединять таблицы по принципу «один ко многим», чего нет у pdoResources. Также переменные, добавленные через PageBlocks, добавляются к результату и выводятся как стандартные переменные ресурса.

      Если это делать через pdoResources:
      {'!pdoResources' | snippet: [
          'parents' => 0,
          'resources' => '1,2,3',
          'tpl' => 'seo-card'
      ]}
      Чанк seo-card
      <div class="col-4 mb-4">
          <div class="card">
              {*изображение храниться в виде json*}
              {set $image = $values.seo_img|fromJSON}
              <img loading="lazy" src="{$image.url}" width="395" height="450" class="card-img-top" alt="{$image.title}">
              <div class="card-body">
                  <h5 class="card-title">{$values.seo_title}</h5>
                  <p class="card-text">{$values.seo_description}</p>
                  {*Получаем данные из таблицы*}
                  {set $list = 'pbCollections'|snippet: ['field_name' => 'seo_list', 'return' => 'json']|fromJSON}
                  <ul class="text-start">
                      {foreach $list as $item}
                          <li>{$item.title}</li>
                      {/foreach}
                  </ul>
              </div>
          </div>
      </div>

      А если через pbResources, то у нас уже будут доступны переменные seo_img, seo_title, seo_description и list (массив)
      Дима Касаткин
      19 февраля 2025, 03:10
      +1
      Вау, какой красивый и короткий код! Там где описане содержимого вкладки — просто магия какая-то! Давненько такого не видел))) Спасибо!

      Из пожеланий, всё-таки не терять MODX-style и дать возможность использовать систему чанков полноценно, не прибегая в foreach циклам в коде шаблонизатора.

      Думаю это выглядело бы примерно так:
      {'!pbResources'|snippet: [
          ...
          'tables' => 'seo_list as list' // Присоединяем таблицу seo_list и заменяем имя на list
          'tpl_list' => 'list-data-item' // С префиксом tpl_ используется alias присоединенной выше таблицы. Если параметр не указан, list будет доступен как массив.
          'tpl' => 'pb-seo-card', // Чанк вывода
      ]}

      Зачем это нужно?

      Кроме избавления от циклов в коде для лаконичности и сохранения удобного MODX-стиля раздельного оформления повторяющихся элементов, который и так изрядно потреплен частым злоупотреблением гибкостью fenom-а) — ну, как минимум, дополнительно можно получить более глубокое кэширование и переиспользовать чанки где-то ещё в проекте.

      А ещё там внутри чанка можно сделать доступным {$idx} и {$total} (его, возможно, и снаружи, с префиксом типа list_total или как-то так) чтобы не городить их опять же в коде чанка, который для разметк (aka верстки) предназначен, а не для логики.

      Это конечно не правка бизнес-логики компонента, а больше к Developer Expierence, но вроде всё так красиво реализовано для разработчиков (уже), что такая вишенка на торт возможно придется кому-то (вроде меня :) ) очень кстати!
        Aleksandr Huz
        19 февраля 2025, 11:34
        +2
        Вау, какой красивый и короткий код! Там где описане содержимого вкладки — просто магия какая-то! Давненько такого не видел)))
        Спасибо.

        Из пожеланий, всё-таки не терять MODX-style и дать возможность использовать систему чанков полноценно, не прибегая в foreach циклам в коде шаблонизатора.
        Вместо этого:
        {foreach $list as $item}
            <li>{$item.title}</li>
        {/foreach}
        можно написать так, если сильно хочется:
        {$list|pbJson:'<li>{$title}</li>'}

        А ещё там внутри чанка можно сделать доступным {$idx} и {$total}
        Но, чтобы вместо инлайнового чанка использовать реальный нужно доработать сниппет. Сделаю

        Чтобы было примерно так:
        {$list|pbJson:'@INLINE <li>{$title}</li>'}
        {$list|pbJson:'list_item_tpl'}
        и в чанке list_item_tpl дополнительно будет доступно {$_idx}, {$_total}, {$_first} и {$_last}
          Дима Касаткин
          19 февраля 2025, 17:57
          0
          Спасибо за отклик!

          Ну хоть что-то, хоть это и немного не то ;)
          Попробую пояснить: Когда я занимаюсь интеграцией макетов с админкой, я включаю «режим разработчика» — открываю документацию (или справку/код других проектов), хожу по шаблонам и расставляю вызовы сниппетов, где в этот момент я полностью работаю с данными — их выводом, преобразованием, разбором массивов и прочим, и раскладываю данные по чанкам. Может даже где-то пишу свои модификаторы вывода для сложных случаев. В этот момент я почти не смотрю на фронтенд, меня мало интересует верстка и стили, главное вытащить нужные данные из админки (из БД, конечно).

          Далее, я иду в чанки, и там уже добавляю к данным оформление. В этот момент я «включаю режим фронтендера верстальщика»: меня в большей тепени беспокоит как выглядят данные, какие отступы, сходится ли с макетами, у меня открыта совсем другая документация (MDN, возможно SASS, дока к моему фронтенд-фреймворку, возможно к каким-то
            Дима Касаткин
            19 февраля 2025, 18:17
            0
            … возможно к каким-то JS-библиотекам и т.п.)

            Короче говоря последнее, что я хочу (и могу) делать на этом этапе, это снова заниматься программированием — разбирать данные из массивов, сверяться с документацией по бекенд-технологиями (таким как PageBlocks, искать там кастомные модификаторы) — мне хватает того, что для простых преобразования в стиле (:lowercase или :ellipsis) у меня открыта документация по фильтрам вывода (в плюс к тому набору выше).

            Поэтому использование в вызове чанка специализированный модификатор (:pbJson) — это прекрасно, что такая возможность в принципе есть, но пока не освоишь инструмент очень глубоко (и не забудешь через год, когда на поддержке вернешься к проекту и надо будет добавить присоединение какой-то таблицы) про это в нужный момент не вспомнишь и встрянешь — это совсем не то же самое, что в сниппет pbResources передать нужные параметры для полноценной подготовки данных для их последующей верстки и оформления. Потому что при любом раскладе, когда работаешь с данными, ты пойдешь в документацию (или код) сниппета и посмотришь возможные параметры, отвечающие за подготовку данных и раскладывание по чанкам. Так почему бы не дать возможность избавиться от программирования в чанке и как альтернативу перенести вызов этого модификатора в подхватывающиеся по шаблону (префиксу) параметры вызова сниппета — тогда вся подготовка данных будет происходить в одном месте (вызове сниппета), а всё оформление — в другом (в чанках). Аналогично тому, как например в mFilter2 можно указывать кастомные чанки row и outer для любых полей, просто добавляя их в параметры вызова прямо по именам, задаваемых в этом же вызове — это почти также красиво и понятно в коде, как твоё @Aleksandr Huz описание содержимого табов в PageBlocks))

            А тот момент, что у одной задачи есть несколько вариантов решений, как раз и делает инструмент по-настоящему гибким!
              Aleksandr Huz
              19 февраля 2025, 19:11
              +1
              Аналогично тому, как например в mFilter2 можно указывать кастомные чанки row и outer для любых полей,
              Я об этом даже не знал. Чтобы что-то узнать, все равно нужно читать документацию, а если нужно универсальное решение — использовать феном.

              Из пожеланий, всё-таки не терять MODX-style и дать возможность использовать систему чанков полноценно, не прибегая в foreach циклам в коде шаблонизатора.
              Теперь я понял, о чем ты. Нужно добавить параметры, как в mFilter2. Например:
              'tpl.outer.list' => 'tpl_outer_name',
              'tpl.row.list' => 'tpl_row_name'
              где list — название переменной.

              Но ведь все равно придется читать доку))
              Но идея хорошая. Сделаю
                Дима Касаткин
                19 февраля 2025, 19:17
                +1
                Да всё верно! Читать доку да, но её не обломно читать когда подготовкой данных занимаешься, а когда верстка разъезжается или js-компоненты шаманишь, оформляя чанки — читать бекендовую доку уже может головы не хватить :)

                Очень рад, что смог донести идею! Спасибо за внимание! Желаю успехов, тебе и компоненту!
        Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
        10