Получение всех TV полей ресурса из конкретной категории и вывод в чанк

Всем привет. Хочу поделиться сниппетом, который позволяет получить все TV поля ресурса из определённой категории и оформить вывод в чанк. Задача по сути простая, но те решения которые находил в интернете (например тут или тут или тут) немного не о том и мне не подходят, тоесть готового кажется нет.

Да, её можно решить с помощью MIGX, но это мне тоже не совсем подходило (об этом ниже). Поэтому решил написать этот сниппет. Единственная сложность была сформировать запрос в БД, так как опыта небыло от слова совсем, поэтому воспользовался gpt чатом, ну а дальше отправить результаты в чанк проблем не составило. Возможно кому-то будет полезно.

Код сниппета следующий:

$q = $modx->newQuery('modTemplateVar');
$q->select($modx->getSelectColumns('modTemplateVar', 'modTemplateVar'));
$q->select('IF(tvc.value != "", tvc.value, tvv.value) AS tv_value');
$q->leftJoin('modTemplateVarResource', 'tvr', 'modTemplateVar.id = tvr.tmplvarid');
$q->leftJoin('modTemplateVarTemplate', 'tvtt', 'modTemplateVar.id = tvtt.tmplvarid');
$q->leftJoin('modTemplateVarResource', 'tvv', 'tvr.contentid = tvv.contentid AND tvr.tmplvarid = tvv.tmplvarid AND tvv.value != ""');
$q->leftJoin('modTemplateVarResource', 'tvc', 'tvr.contentid = tvc.contentid AND tvr.tmplvarid = tvc.tmplvarid');
$q->where([
    'tvr.contentid' => $id, // id ресурса у которого запрашиваем TV
    'modTemplateVar.category' => $category, // id категории, по которой сгруппированы наши TV
]);
$q->sortby('rank', 'ASC');

$resources = $modx->getCollection('modTemplateVar', $q);

foreach($resources as $resource){
    $out .= $modx->getChunk($tpl, [
        'caption'=> $resource->get('caption'), 
        'rating'=> $resource->get('tv_value')
    ]);
}

return $out;

Вызов в шаблоне:

[[!tvGroup?
    &id=`[[*id]]`
    &tpl=`tvGroupTpl`
    &category=`45`
]]

Тут стоит сказать, что если в вызов сниппета вписать ID категории из админки, работать не будет, так как в базе данных у нужной мне группы TV полей идентификатор другой. Чтобы всё работало нужен тот, который в БД, в столбце category.

Ну и код чанка для полноты картины:

<div class="item-parent">
    <div class="row">
        <div class="col-9 pr-0">
            <span class="caption">[[+caption]]:</span>
            <div class="item-bg" data-rating="[[+rating]]"></div>
        </div>
        <div class="col-3">
            <span class="rating">[[+rating]]/10</span>
        </div>
    </div>
</div>

В результате я получил нужную мне группу TV полей и оформил вывод в чанк.

Вот так это выглядит в админке (просто список полей):



И вот так выглядит на сайте:



Хочу сразу ответить на некоторые вопросы.

1. По сути я мог бы просто напрямую вывести каждое TV в шаблон и забыть, но потом это было бы неудобно структурировать и стилизовать, учитывая что полей 19 и потенциально их может быть больше (поэтому и захотелось сделать вывод в чанк).

2. Я мог бы воспользоваться MIGX и тут было бы 2 сценария:
  • Заказчику для каждого ресурса (ресурсов пока 314) пришлось бы 19 раз нажимать на кнопку «Добавить характеристику» — что точно не вариант.
  • Я мог бы сделать json разметку по принципу:
    {"caption":"Характеристики шкалой","fields":[
    {"field":"helth","caption":"Здоровье"},
    {"field":"intel","caption":"Интеллект"},
    {"field":"grooming","caption":"Потребность в груминге"},
    ...
    ]}
    ]
    и заказчику оставалось бы только наполнить эти поля, но мне не хотелось чтобы тот, кто будет наполнять в какой-то момент «затроил» и насоздавал дополнительные экземпляры этих полей в одном ресурсе.
Я почти уверен что можно сделать более изящнее, или есть какое-то решение, которое я так и не нашел, поэтому буду рад посмотреть на другие варианты реализации и любой конструктивной критике.
Андрей Чаплыгин
03 мая 2024, 23:25
modx.pro
3
567
+5

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

Павел Романов
04 мая 2024, 13:27
2
+3
Так вроде, пошустрее будет ):
$sql = "
    SELECT tvr.value, tv.caption
    FROM {$modx->getTableName('modTemplateVarResource')} tvr
    LEFT JOIN {$modx->getTableName('modTemplateVar')} tv
    ON tv.id = tvr.tmplvarid 
    WHERE tvr.contentid = {$id} AND tv.category = {$category}
";
$q = $modx->prepare($sql);
$q->execute();
$resources = $q->fetchAll(PDO::FETCH_ASSOC);
foreach($resources as $resource){
    $out .= $modx->getChunk($tpl, $resource);
}
return $out;

В чанке [[+caption]] и [[+value]]:
<div class="item-parent">
    <div class="row">
        <div class="col-9 pr-0">
            <span class="caption">[[+caption]]:</span>
            <div class="item-bg" data-rating="[[+value]]"></div>
        </div>
        <div class="col-3">
            <span class="rating">[[+value]]/10</span>
        </div>
    </div>
</div>

Кстати, верный ID категории можно узнать в Элементы » Категории.
    Василий Наумкин
    05 мая 2024, 09:18
    +2
    Примерно тоже самое, только при помощи mmxDatabase:

    $id = $modx->getOption('id', $scriptProperties);
    $category = $modx->getOption('category', $scriptProperties, '1', true);
    
    $resource = \MMX\Database\Models\Resource::query()
        ->select('id', 'pagetitle')
        ->with('TvValues', static function($c) use ($category) {
            $c->select('value', 'contentid', 'tmplvarid');
            $c->whereHas('Tv', static function($c) use ($category) {
                $c->where('category', $category);
            });
            $c->with('Tv:id,name,caption,default_text');
        })
        ->find($id);
    
    return $resource ? print_r($resource->toArray(), true) : 'Not found';

    Получается 3 простых выборки, без join.

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

    Eloquent собирает все данные вложенными массивами в итоговый результат:


    Дальше можно перебирать результат на Fenom со всеми проверками на пустоту и прочее.
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      2