MODX. Дополнительные поля (TV)

Дополнительные поля (Template Variables) — элементы для расширения стандартных полей ресурса.

Вкладка «Параметры ввода»

Параметры ввода — тот вид поля, который будет отображаться в админ панели, при редактировании ресурса. Есть несколько заранее предустановленных параметров, но если их не хватает, можно добавить разнообразия с помощью дополнительных компонентов, вроде MIGX.

Стандартные варианты параметров ввода: 

  • Авто-метка (auto-tag) (список элементов подобных тегам)
  • Дата (date)
  • Изображение (image)
  • Переключатели (radio)
  • Скрытый (hidden) (не отображается в админ. панели и на сайте)
  • Список (одиночный выбор) / listbox (single-select) 
  • Список (множественный выбор) / listbox (multi-select) 
  • Список ресурсов (resource list) (из структуры сайта)
  • Тег (tag)
  • Текст (text) (обычная текстовая строка)
  • Текстовая область (textares) (обычная текстовая область)
  • Текстовый редактор (richtext) (текстовая область с визуальным редактором)
  • Устаревший список множественного выбора (legasy multiple list)
  • Файл (область для вставки файла)
  • Флажки (checkbox) — если флаг не отмечен, не отправляется никакого значения. Стоит учитывать это при продумывании архитектуры, иногда, это становится проблемой.
  • Число (number)
  • Электронная почта (email)

Значения полей

Чтобы добавить в дополнительное поле значение, надо указать его в поле “Input Option Values”.

Если значений несколько, варианты записываются с разделителем: Значение1||Значение2||Значение3

Возможна запись формата ключ => значение: Значение1==cod1||Значение2==cod2||Значение3==cod3

Проще говоря, формат записи таков DISPLAY VALUE==VALUE

  • DISPLAY VALUE — значение отображаемое на странице ресурса в админке;
  • VALUE — значение которое будет записано в базе.

Варианты по умолчанию

Запись параметров по умолчанию делается с помощью поля “Default Value”, а формат записи зависит от типа поля.

  • checkbox: наличие галки — cod1 (символьный код поля), отсутствие галки — ничего. Если значений несколько, варианты записываются через запятую — cod1, cod3
  • date: можно использовать текстовое обозначение, или привязку для полей, а можно указать слово:
    • today — тогда будет отображаться сегодняшняя дата.
    • now — текущая дата и время.
    • yesterday — вчера.
    • tomorrow — завтра.
    • + / - (любое число)h — почему-то в перепутанном порядке, + 24h высчитывается как (сегодня - 24 часа), и наоборот со знаком минус.

Варианты привязок для полей (@binding)

При использовании дополнительных полей можно использовать привязки, т.е. если перевести на человеческий - можно указывать значения не только руками, но и брать их из базы, из файлов, чанков и т.п. Для этого в поле "варианты значений", или "значение по умолчанию" надо вставить конструкции следующего типа:

@CHUNK chunkName список из чанка с указанным именем
@RESOURCE id список из ресурса с указанным id
@DIRECTORY ./ список файлов из указанной директории
@FILE ../assets/test.txt список из файла
@SELECT pagetitle, id FROM [[+PREFIX]]site_content WHERE isfolder==0 получение значений из базы данных
@EVAL return $modx->runSnippet('snippetName') получение списка посредством выполнения PHP кода (ходит слух что этой привязки в MODX 3 не будет).

Примеры вывода дополнительных полей

С помощью тегов MODX и phx

Если у поля одно значение:

[[*tvName]]

Вывод названия дополнительного (TV) поля:

[[*tvname:tvLabel]]

Если у поля множество значений:

[[*tvName:contains=`value1`:then=``]]

Или напимер так:

Fenom
Теги MODX
{if $_modx->resource.tvValue == 1} 
 <p>Какой-то текст или код</p>
{/if}
[[*tvValue:is=`small`:then=`[[$small]]`:else=`[[$big]]`]]

Если у дополнительного поля установлен формат ввода «Список ресурсов», то результатом вывода будет установленный id ресурса. Для вывода какого-либо значения по id можно использовать FastField:

Fenom
Теги MODX
{$tvListRes | resource : 'pagetitle'}
[[#[[*tvListRes]].pagetitle]]

Дополнительные поля в БД

  • _site_tmplvars — параметры того или иного поля.
  • _site_tmplvar_access — политика доступа того или иного поля.
  • _site_tmplvar_contentvalues — контентное содержание доп полей.
  • _site_tmplvar_templates — шаблоны для которых доступно то или иное доп поле.

Работа с доп. полями при помощи xPDO

Создание текстового доп. поля:

$name = 'any_tv'; // имя дополнительного поля
$tempID = '1,2'; // ID шаблонов, для которых поле будет доступно

// проверяем есть ли объект с таким именем, и создаем новый, если нет
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}

// определяем настройки будущего дополнительного поля
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'text',
    'caption'      => 'Имя ТВ', 
    'description'  => 'Описание ТВ', 
    'category'     => 0 
));

// сохраняем
$tv->save();

// собираем в массив для дальнейшей модернизации
$tvs[] = $tv->get('id').'-'.$tempID;  

У каждого типа поля свой набор настроек, соответственно, код местами будет отличаться:

TV с изображением

$name = 'image';
$tempID = '1'; 
$source = 2; // Id источника файлов для TV
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'image',
    'caption'      => 'Изображение',
    'description'  => 'Описание ТВ',
    'category'     => 0
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

/* Источник файлов для TV */
$sourceElement = $modx->newObject('sources.modMediaSourceElement');
$sourceElement->fromArray(array(
    'object' => $tv->get('id'),
    'context_key' => 'web',
), '', true, true);
$sourceElement->set('source', $source);
$sourceElement->save();
/* end Источник файлов для TV */

TV списка одиночного выбора

$name = 'select';
$tempID = '2'; 
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'listbox',
    'caption'      => 'Рейтинг отзыва',
    'elements'     => '1==1||2==2||3==3||4==4||4.5==4_5||5==5',
    'category'     => 0,
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

TV чекбокса множественного выбора

$name = 'category_main';
$tempID = $tempIDCategoryDop; 
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'checkbox',
    'caption'      => 'Показывать',
    'elements'     => 'Да==Да||Нет==Нет',
    'category'     => 0,
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

TV для компонента FastUploadTV

$name = 'fastImage';
$tempID = '17,6,9,14,12'; 
$source = 2; // Id источника файлов для TV
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'fastuploadtv',
    'caption'      => 'Изображение',
    'category'     => 0,
    'source'       => $source,
    'input_properties' => array(
                            "path" => "id/{id}/",
                            "prefix" => "",
                            "MIME" => "",
                            "showValue" => true,
                            "showPreview" => true
                        ),
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

/* Источник файлов для TV */
$sourceElement = $modx->newObject('sources.modMediaSourceElement');
$sourceElement->fromArray(array(
    'object' => $tv->get('id'),
    'context_key' => 'web',
), '', true, true);
$sourceElement->set('source', $source);
$sourceElement->save();
/* end Источник файлов для TV */

TV для компонента MIGX

$name = 'slider';
$tempID = '2'; 
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'migx',
    'caption'      => 'Слайдер',
    'category'     => 0,
    'input_properties' => array(
                            "formtabs" => '[{"caption":"Gallery","fields": [{"field":"img","caption":"Картинка","inputTV":"image"},{"field":"link","caption":"Ссылка"]]}}',
                            "columns" => '[{"header": "Картинка","dataIndex": "img","renderer":"this.renderImage","width":"100"},{"header": "Ссылка","dataIndex": "link","width":"400"]}'
                        ),
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

TV для компонента YandexMaps

$name = 'mapyandex';
$tempID = '2'; 
if (!$tv = $modx->getObject('modTemplateVar', array('name' => $name))) {
    $tv = $modx->newObject('modTemplateVar');
}
$tv->fromArray(array(
    'name'         => $name,
    'type'         => 'yandexMaps',
    'caption'      => 'Метка компании на карте',
    'category'     => 0,
));
$tv->save();
$tvs[] = $tv->get('id').'-'.$tempID;

Назначение шаблонов доп полям (если создается несколько доп полей, данный блок кода должен идти последним):

foreach ($modx->getCollection('modTemplate') as $template) 
{
    $templateId = $template->id;
    foreach ($tvs as $k => $tv) {
        $tvOpt = explode("-", $tv);
        $tvid = $tvOpt[0];
        $tvTemps = explode(",",$tvOpt[1]);
        
        foreach ($tvTemps as $tvTemp){
            if($templateId == $tvTemp){
                if (!$tvt = $modx->getObject('modTemplateVarTemplate', array('tmplvarid' => $tvid, 'templateid' => $templateId))) {
                    $record = array('tmplvarid' => $tvid, 'templateid' => $templateId);
                    $keys = array_keys($record);
                    $fields = '`' . implode('`,`', $keys) . '`';
                    $placeholders = substr(str_repeat('?,', count($keys)), 0, -1);
                    $sql = "INSERT INTO {$modx->getTableName('modTemplateVarTemplate')} ({$fields}) VALUES ({$placeholders});";
                    $modx->prepare($sql)->execute(array_values($record));
                }
            }
        }
    }
}

Заполнение дополнительного поля через xPDO:

// Выбираем ресурс 
$page = $modx->getObject('modResource', 1);

// устанавливаем значение
if (!$page->setTVValue('any_tv', 'Какой-то текст')) {
    $modx->log(1, 'Произошла ошибка при сохранении ТВ');
}

Получение значения дополнительного поля через xPDO:

// Выбираем ресурс 
$page = $modx->getObject('modResource', 1);

// выводим значение
echo $page->getTVValue('any_tv');

Удаление дополнительного поля через xPDO:

$tv = $modx->getObject('modTemplateVar', array('name' => 'any_tv'));
$tv->remove();

Получение ID созданного доп поля:

// $tv->save();
$id = $tv->get('id');

Выборка ресурсов с TV-параметрами:

// запрашиваем ресурсы с указанными параметрами
$q = $modx->newQuery('modResource');
$q->leftJoin('modTemplateVarResource', 'Price', 'modResource.id = Price.contentid AND Price.tmplvarid = 10');
$q->leftJoin('modTemplateVarResource', 'Country', 'modResource.id = Country.contentid AND Country.tmplvarid = 11');
$q->groupby('modResource.id');


$q->select([
    'modResource.*',
    '`Price`.`id` AS `Price_id`, `Price`.`tmplvarid` AS `Price_tmplvarid`, `Price`.`contentid` AS `Price_contentid`, `Price`.`value` AS `Price_value`',
    '`Country`.`id` AS `Country_id`, `Country`.`tmplvarid` AS `Country_tmplvarid`, `Country`.`contentid` AS `Country_contentid`, `Country`.`value` AS `Country_value`',
    'IFNULL(`Price`.`value`, 0) AS `Price_value`',
    'IFNULL(`Country`.`value`, "RUS") AS `Country_value`'
]);

$q->where([
    [
        'Price.value:!=' => 0,
        'Price.value:IS NOT' => NULL
    ], [
        'Country.value' => 'RUB',
        'OR:Country.value:IS' => NULL
    ]
]);

$count = $modx->getCount('modResource', $q);
$q->limit(100);
if ($q->prepare() && $q->stmt->execute()) {
	$resources = $q->stmt->fetchAll(PDO::FETCH_ASSOC);
}

echo  "<p>Всего ресурсов найдено: {$count}</p><hr>";

foreach($resources as $resource) {
    echo "<p><b>{$resource['pagetitle']} ({$resource['id']})</b> -
    {$resource['Price_value']} {$resource['Country_value']}</p>";
}

 

tnx

За xPDO схемы спасибо Илье Уткину