Создание своего провайдера
Описание
Провайдер должен наследовать класс \Bitrix\DocumentGenerator\DataProvider.
При желании можно наследовать существующий провайдер, переопределить нужные методы, добавить его в список и он будет фигурировать там наравне с предустановленными.
У класса \Bitrix\DocumentGenerator\DataProvider есть несколько вспомогательных наследников:
\Bitrix\DocumentGenerator\DataProvider\EntityDataProvider- для работы с таблетами ORM;\Bitrix\DocumentGenerator\DataProvider\HashDataProvider- для работы с простыми провайдерами в виде хеша;\Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider- предоставляет доступ к множественным значениям одного поля.
Цепочка провайдеров
У каждого провайдера может быть указан родительский провайдер. Если провайдер был инициализирован автоматически из описания поля, то у него будет указан родительский провайдер. Или его можно указать вручную через \Bitrix\DocumentGenerator\DataProvider::setParentProvider($dataProvider);.
Проверки
Проверка прав доступа
Проверка прав осуществляется методом \Bitrix\DocumentGenerator\DataProvider::hasAccess($userId);.
Проверка доступа вызывается в \Bitrix\DocumentGenerator\Document::hasAccess() каждый раз при работе с документом. Это не абстрактный метод. Если метод не определен, но у текущего провайдера есть родитель - этот метод будет вызван у родительского провайдера. Если родителя нет - вернет false.
Проверка успешной загрузки данных провайдера
Модуль documentgenerator спроектирован таким образом, что экземпляры провайдеров могут иметь два состояния:
- До загрузки данных. В этом состоянии провайдер отдает список своих полей "обезличенно".
- После загрузки данных. В этом состоянии провайдер может отдать "актуальный" список своих полей и их значения.
В исходном классе нет метода для получения данных (но есть в наследнике \Bitrix\DocumentGenerator\DataProvider\EntityDataProvider).
Программист должен сам решить, как и когда эти данные должны загружаться (из соображений производительности). В некоторых случаях данные должны загружаться прямо в конструкторе, в некоторых - перед получением списка полей и т.д.
Для хранения данных есть атрибут $data. Исходный метод определения загруженности провайдера:
/**
* @return bool
*/
public function isLoaded()
{
return $this->data !== null;
}
Этот метод вызывается в методе проверки прав и при получении значения. Если на момент обращения к этим методам провайдер не будет загружен - проверка прав вернет false, а значение не будет получено.
Поля провайдера
Основной метод любого провайдера - getFields. В целях улучшения производительности рекомендуется сделать так, чтобы основной код метода исполнялся только один раз, т.к. метод провайдера getFields() вызывается несколько раз в процессе формирования документа.
public function getFields()
{
if($this->fields === null)
{
parent::getFields();
$this->fields['MY_FIELD'] = ['TITLE' => 'My Field Title',];
}
return $this->fields;
}
Он должен возвращать массив, где ключ - название поля (лучше использовать английские буквы в верхнем регистре + подчеркивание), а значение - его описание. Описание поля может содержать следующие ключи (все они не обязательны, можно оставить пустой массив):
- TITLE - заголовок поля (если его нет - выводится название)
- TYPE - тип поля. Может принимать значения:
- IMAGE - поле является путем к файлу-картинке
- STAMP - аналогично IMAGE, но это поле является подписью или печатью
- DATE - будет сформирован объект класса
\Bitrix\DocumentGenerator\Value\DateTime - TEXT - обычное поле, но на странице изменения документа вместо input будет textarea
- NAME - будет сформирован объект класса
\Bitrix\DocumentGenerator\Value\Name - PHONE - будет сформирован объект класса
\Bitrix\DocumentGenerator\Value\PhoneNumber - также сюда можно в явном виде передать полное имя класса-наследника
\Bitrix\DocumentGenerator\Value - FORMAT - формат по умолчанию для наследников
\Bitrix\DocumentGenerator\Value - VALUE - описание способа получения значения. Само значение по умолчанию берётся из
$data. В качестве значения может выступать: - собственно, само значение поля в виде числа или строки
- строка, содержащая название другого поля - в этом случае будет сделана попытка получить значение этого поля из другого поля с соответствующим названием. (пример - поле COMPANY_NAME из
\Bitrix\Crm\Integration\DocumentGenerator\DataProvider\Lead) - callable-конструкция. В этом случае для получения значения будет вызван этот метод/функция
- Closure (анонимная функция). Аналогично предыдущему пункту будет вызвана при получении значения. Но если вы пишете значения полей в атрибуты объекта, то придётся отказаться от этого варианта, лучше сделать соответствующий метод и указать его имя в явном виде
- массив значений. В этом случае в шаблон попадёт объект
\Bitrix\DocumentGenerator\Value\Multiple - PROVIDER - если значение поля должно быть другим провайдером, то здесь надо указать полное имя соответствующего класса.
- OPTIONS - массив $options, который будет передан в конструктор провайдера.
- VALUES - массив значений, которые будут переданые в созданный по этому описанию провайдер и переопределят его значения.
- COPY - указатель на описание другого поля. Если вы хотите иметь несколько полей с одинаковым содержимым, но разным названием, то можно сделать это с помощью этого ключа. Например, у вас есть основное поле, это провайдер с множеством опций. Вы хотите, чтобы доступ к этому полю мог быть получен через разные названия полей. В этом случае создаются новые поля, где указываются только TITLE, VALUE и OPTIONS[COPY], равные названию основного поля.
Итоговое значение
За получение значений полей отвечает метод \Bitrix\DocumentGenerator\DataProvider::getValue().
Если по каким-то причинам не устраивает получение значения поля из его описания, то можно переопределить этот метод. Например, так:
public function getValue($name)
{
if($name == 'myComplexFieldValue')
{
return 'complexValue';
}
return parent::getValue($name);
}
Но класс спроектирован таким образом, что метод получения значения можно указать в описании поля, поэтому переопределять этот метод не рекомендуется.
Исходная реализация сначала ищет значение поля в $this->data и если его нет - пытается его получить из описания поля.
Само вычисление значения выполняет \Bitrix\DocumentGenerator\DataProviderManager::getDataProviderValue().
Порядок получения значения следующий:
- Считывается описание поля из
\Bitrix\DocumentGenerator\DataProvider::getFields(). Если такого поля нет - вернетсяfalse. - Если в $options провайдера есть ключ VALUES и в нём есть значение этого поля, то значение берется оттуда.
- Если в VALUE описании поля строка - значение берется как
getValue()текущего провайдера с этой строкой - Если в VALUE - callable-конструкция, то она вызывается для получения значения. На вход этого метода/функции всегда идёт название поля
- Если в описании поля есть PROVIDER, то создается новый экземпляр этого класса, на вход ему передается вычисленное значеие, а в $options - передаются OPTIONS из описания поля
После получения значения над ним вызывается \Bitrix\DocumentGenerator\DataProviderManager::prepareValue().
Этот метод преобразует полученное значение к соответствующему типу из описания поля. Если вычисленное значение уже экземпляр \Bitrix\DocumentGenerator\Value, то он возвращается как есть.
Множественное значение
Возможна ситуация, когда в значении одного поля может быть несколько вариантов. Особенно это актуально для полей-провайдеров. Например, поле "моя компания" в провайдерах CRM. Моих компаний может быть несколько, и пользователь должен иметь возможность быстро сменить используемую компанию в документе.
Результат вычисления значения (обычно это результат выполнения callable-конструкции в описании поля) должен иметь вид простого массива, где каждое значение - это массив вида:
array( 'VALUE' => $value, // само значение 'TITLE' => $title, // заголовок, который будет выводиться в форме изменения документа для этого пункта 'SELECTED' => $selected, // true - если должно использоваться это значение по умолчанию );
Если SELECTED у всех false, то при формировании документа будет выбран первый вариант из списка.
Значения в виде массивов
Некоторые поля могут иметь несколько значений одновременно. Значения таких полей вставляются внутрь таблиц/повторяющихся блоков. Например, товары, налоги, контакты сделки.
В этом случае описание поля должно быть сформировано следующим образом:
$this->fields['LIST'] = [ 'PROVIDER' => \Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider::class, 'TITLE' => 'My List', 'OPTIONS' => [ 'ITEM_PROVIDER' => 'MyItemDataProvider::class', // полное имя класса-провайдера отдельного элемента 'ITEM_NAME' => 'ITEM', // название поля, по которому будет идти обращение к элементу 'ITEM_TITLE' => 'My Item', // заголовок элемента ], 'VALUE' => [$this, 'loadItems'], ];
Метод loadItems должен вернуть простой массив сформированных провайдеров.