Допустим, вы прочитали инструкции тут и тут. Создали свой высоконагруженный инфоблок (по сути – почти обычную таблицу) и у вас в этом highload-блоке есть поле, в котором хранится привязка сущностей этого блока к ID элементов старых добрых инфоблоков. Поле это будет целочисленным и множественным и необязательным.
Чтобы общаться с этой таблицей, мы в каком-нибудь модуле опишем класс в папке lib. В этом классе будет метод getMap, который опишет все поля, которые мы создали в таблице.
Описание проблемы
Теперь мы попробуем создать запись в таблице вот примерно так
\ПространствоИмён\ИмяКласса::Add([
//упустим остальные параметры
'ИМЯ_МНОЖЕСТВЕННОГО_ПОЛЯ' => [
123123,
324234
]
])
Запись может пройти все проверки (вы ведь описали Valdator для каждого поля в карте (метод getMap)?), но в интерфейсе админки битрикса, при просмотре данных в инфоблоке никаких значений во множественном поле не будет.
Вызов
ИмяКласса::getList()->fetchAll()
вернёт все наши записи. И во множественном поле тут также будет пусто. Возможна еще такая ситуация, когда мы передаём только один ID
\ПространствоИмён\ИмяКласса::Add([
//упустим остальные параметры
'ИМЯ_МНОЖЕСТВЕННОГО_ПОЛЯ' => 3213123
])
В этом случае в админке будет пусто, но getList вернёт нам значение этого поля. В чем же подвох?
Решение проблемы
Тут всё просто — множественные поля должны сериализовываться. Странно, что в документации этого нет. Можно сильно упростить себе жизнь, описав грамотную карту таблицы. Например, если указать в описании поля параметр ‘serialized’ => true, то битрикс сам будет сериализовывать все данные перед сохранением в таблицу и при возврате. С этим параметром и в админке и в коде всё будет отображаться нормально.
НО! Возникает проблема – если ID у нас в одном экзепляре, то хотелось бы передать только его, не засовывая в массив. Но при этом, чтобы возвращался в коде всегда массив, даже если там только одно значение, чтобы не проверят каждый раз тип данных. В этом нам поможет параметр ‘save_data_modification’, который может принудительно запихнут число в массив.
Получается, что поле в методе getMap должно быть описано примерно так.
new Entity\IntegerField('ИМЯ_МНОЖЕСТВЕННОГО_ПОЛЯ', [
'column_name' => 'UF_ИМЯ_МНОЖЕСТВЕННОГО_ПОЛЯ',
'serialized' => true,
//принудительно сохраняем данные в массив
'save_data_modification' => function(){
return [
function($value){
if(!is_array($value)){
return [$value];
}else{
return $value;
}
}
];
},
'validation' => function() {
return [
function($value){
if(is_array($value)){
$result = true;
foreach($value as $item){
if(!(is_numeric($item) and intval($item) > 0)){
$result = false;
}
}
if($result){
return $result;
}else{
return 'Некорректный массив с ID элементов';
}
}elseif(is_numeric($value) and intval($value) > 0){
return true;
}else{
return 'Некорректный ID элемента, ожидается целое число или
массив целых чисел.';
}
}
];
}
]),
И тогда всё будет удобно и красиво =)
6 комментариев
Небольшая особенность в setSelect нужно обязательно указывать runtime-поле, что добавляет в select все поля связанной таблицы.
Ответить
> Чтобы общаться с этой таблицей
На сколько мне известно, такой подход следует использовать при создании в БД собственных таблиц, минуя битрикс. Тогда действительно можно описать класс-модель в стиле D7, чтобы иметь возможность работать со своей таблицей по правилам битрикса (ORM D7).
Однако в вашем примере речь идет про работу с HL-иблоками, и для этого достаточно создать динамическую сущность с указанием параметров HL-иблока:
$HLEntityModelClass = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity(..)
Дальше можно будет делать выборки и добавлять/изменять данные.
При записи значений множ. св-в битрикс сам будет их сериализовать и(!) дублировать в отдельные поисковые таблицы для множественных св-в данного HL-иблока, чего в вашем случае не будет происходить.
Вся суть HL-иблоков:
— хранить только нужные данные (именно поэтому нужно создать каждое поле руками, и нет базовых)
— быстрые поисковые запросы по св-м, и(!) множественным св-м (за счет внесения избыточности, и хранения множ св-в построчно в отдельной поисковой таблице, отдельно для каждого HL-иблока)
—
То есть в вашей статье перепутались две отдельные темы:
— работа со своими таблицами через битрикс ORM
— работа HL-иблоками.
Поправьте меня, если я не прав :)
Ответить
P.S.
очень жаль, что у вас вырезаются все пустые строки — без этого текст становится нечитаемым.
добавьте пожалуйста разрывы на абазацы, как в оригинальном сообщении
Ответить
Добавил отступ снизу у параграфов, стало лучше читаться.
Ответить
HL блок это лишь более менее удобный GUI для обычных таблиц, которые изредка необходимо править в админке. Он будет такой же быстрый как и любая обычная таблица (при правильных типах полей, индексах и построении запросов). Да, по сравнению с инфоблоками, выигрыш в скорости будет огромный, особенно при больших объемах данных. Но это обычная таблица, все столбцы которой описаны в пользовательских полях, за счет чего их можно вывести в админке в пригодном для редактирования виде.
HL инфоблок становится удобным, когда начинаешь использовать его в ORM. Создаёшь описание всех полей, задаёшь валидаторы, модификаторы и прочее. В этом случае огромное количество копипаста уходит в маппинг таблицы. И многие рутинные задачи начинают решаться проще. Мы уже точно знаем что метод add проверит валидность поля и приведёт его к нужному виду. Это сильно упрощает код. В этом случае наличие в админке GUI для HL инфоблоков лишь удобная вишенка на торте, основная цель была именно использовать ORM. В статье я описал некоторые нюансы этого подхода. Можно было бы просто создать обычную таблицу и не заморачиваться с HL.
Ответить
вообще ни разу, тк все это собирается не квери билдером а орм. И если у тебя есть множественное поле или енам список…то смысл хайлоада теряется. Он тебе тучу запросов будет кидать вместо 1го
Ответить