Создание своего провайдера
Описание
Провайдер должен наследовать класс \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
должен вернуть простой массив сформированных провайдеров.