Кастомизация
Добавление провайдера и данных
Про создание своего провайдера можно почитать тут.
Регистрируем обработчик получения списка провайдеров:
//Для модуля \Bitrix\Main\EventManager::getInstance( )->registerEventHandler('documentgenerator', 'onGetDataProviderList', 'crm', '\Bitrix\Crm\Integration\DocumentGeneratorManager', 'getDataProviders'); //рантайм \Bitrix\Main\EventManager::getInstance( )->addEventHandler('documentgenerator', 'onGetDataProviderList', ['myClass', 'addDocumentProviders']);
Обработчик должен возвращать массив вида:
$result[$className] = [ 'NAME' => 'Языковое название', 'CLASS' => $className, 'MODULE' => 'myModule', ];
где $className - полное название класса с неймспейсом.
Это должен быть наследник класса \Bitrix\DocumentGenerator\DataProvider
.
Добавление дополнительных данных в результат \Bitrix\DocumentGenerator\Document::getFile()
В своём наследнике класса \Bitrix\DocumentGenerator\DataProvider
определяем метод getAdditionalDocumentInfo()
.
Этот метод должен вернуть массив, который будет подмешан в результат \Bitrix\DocumentGenerator\Document::getFile()
.
Изменение значений полей
Изменение значений полей документа перед генерацией (с documentgenerator 18.0.6)
Подписываемся на событие:
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onBeforeProcessDocument', 'onBeforeProcessDocument'); function onBeforeProcessDocument($event) { $document = $event->getParameter('document'); /** @var \Bitrix\DocumentGenerator\Document $document */ // добавить дополнительные описания полей // $document->setFields($newFields); // добавить значения полей //$document->setValues(['someField' => 'myCustomValue']); // получить список полей и их текущих значений //$fields = $document->getFields(); }
Обработчик события может ничего не возвращать, все действия производятся над объектом документа.
С объектом следует быть осторожнее. Если в обработчике вызвать $document->getFile()
может возникнуть бесконечная рекурсия.
Изменение списков
Изменение содержимого списков (с версии documentgenerator 20.0.0)
Раньше для изменения полей списков в событии onBeforeProcessDocument
приходилось пользоваться рефлексией.
Теперь добавлены несколько новых полезных методов:
Метод | Описание |
---|---|
ArrayDataProvider::getItemByIndex(int $index) | Метод вернет элемент списка по индексу (если он есть). |
ArrayDataProvider::replaceItem(int $index, $item) | Метод заменит элемент списка с индексом $index на $item .
Если элемента списка по этому индексу нет, то будет выброшено исключение |
ArrayDataProvider::addItem($item): int | Метод добавит новый элемент в конец списка. Вернет индекс нового элемента. |
ArrayDataProvider::deleteItemByIndex(int $index) | Метод удалит элемент по индексу $index , после чего все элементы будут сдвинуты. |
HashDataProvider::getData()x) | Метод вернет значения всех полей провайдера. |
HashDataProvider::setData(array $data) | Метод перезапишет значения полей полностью. |
Ниже небольшой пример, как с помощью новых методов можно убрать все товары, кроме первого. А у оставшегося поменять название
\Bitrix\Main\EventManager::getInstance()->addEventHandler( \Bitrix\DocumentGenerator\Driver::MODULE_ID, 'onBeforeProcessDocument', function(\Bitrix\Main\Event $event) { $productsPlaceholder = 'PRODUCTS'; /** @var \Bitrix\DocumentGenerator\Document $document */ $document = $event->getParameter('document'); $provider = $document->getProvider(); $productsProvider = $provider->getValue($productsPlaceholder); if($productsProvider instanceof \Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider) { foreach($productsProvider as $index => $product) { if($index > 0) { $productsProvider->deleteItemByIndex($index); } else { $data = $product->getData(); $data['NAME'] = 'New Product Name'; $product->setData($data); } } } } );
Отслеживание изменений документов
Подписываемся на события
// создание документа (уже после сохранения записи в БД) \Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onCreateDocument', ['myClass', 'onCreateDocument']); // изменение документа \Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onUpdateDocument', ['myClass', 'onUpdateDocument']);
В обоих случаях, аналогично onBeforeProcessDocument
, в событие придёт параметр $document с объектом документа.
// окончание конвертации документа \Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onDocumentTransformationComplete', ['myClass', 'onDocumentTransformationComplete']);
В обработчик этого события придут два параметра - $documentId и $data.
$documentId - ID документа, по нему документ можно поднять через \Bitrix\DocumentGenerator\Document::loadById($documentId)
$data - результат \Bitrix\DocumentGenerator\Document::getFile()
.
Свои модификатор и хранилища
Чтобы сделать обработку своих провайдеров необходимо написать наследника \Bitrix\DocumentGenerator\Value
.
Поле провайдера должно вернуть сформированный объект, либо в описании поля должен быть указан тип и формат по умолчанию.
Лучше всего делать по образцу (например \Bitrix\DocumentGenerator\Value\DateTime
).
Использование своего хранилища документов
Сначала необходимо реализовать класс, который будет воспроизводить интерфейс \Bitrix\DocumentGenerator\Storage
. Дальше можно либо через событие onBeforeProcessDocument
менять хранилище документа перед его обработкой:
$storage = new MyStorage(); $document->setStorage($storage);
либо сохранить значение хранилища по умолчанию через опцию
\Bitrix\Main\Config\Option::set('documentgenerator', 'default_storage_type', MyStorage::class);
Перед реализацией рекомендется посмотреть существующие классы \Bitrix\DocumentGenerator\Storage\File
, \Bitrix\DocumentGenerator\Storage\BFile
, \Bitrix\DocumentGenerator\Storage\Disk
.
Отслеживание просмотра
Отслеживание просмотра публичной ссылки на документ (с documentgenerator 18.6.0)
При просмотре публичной ссылки отправляется php-событие
\Bitrix\Main\EventManager::getInstance()->send(new \Bitrix\Main\Event('documentgenerator', 'onPublicView', [ 'document' => $document, 'isPublicView' => false, ]));
где:
- $document - объект
\Bitrix\DocumentGenerator\Document
; - isPublicView - Параметр доступен с версии documentgenerator 20.100.0 и принимает значение True или False. Параметр равен
true
, если публичная ссылка просмотрена не сотрудником портала в первый раз.
Чтобы узнать, документ какого модуля был использован, над дернуть шаблон документа
$template = $document->getTemplate(); if($template && $template->MODULE_ID == 'crm') { // your code for crm here }
Если надо отследить документа для CRM и получить данные об источнике документа, можно воспользоваться следующим кодом:
$provider = $document->getProvider(); if($provider && $provider instanceof \Bitrix\Crm\Integration\DocumentGenerator\DataProvider\CrmEntityDataProvider) { $ownerId = $provider->getSource(); $ownerType = $provider->getCrmOwnerType(); }
Новое с версии 20.0.0
Переопределение логики
Модуль позволяет использовать свои классы вместо предустановленных. Таким образом можно изменить поведение модуля.
Чтобы это сделать, необходимо подписаться на событие onDriverCollectClasses
модуля documentgenerator
.
В результате события должен прийти массив, где ключ - это имя класса, а значение - это ваш наследник.
Например, вы хотите использовать свою реализацию Document
, чтобы использовать свой алгоритм генерации номеров вместо встроенного нумератора.
Для этого создаете своего наследника:
class MyDocument extends Bitrix\DocumentGenerator\Document { public function getNumber(bool $preview = true): string { return 'my_super_smart_number'; } }
После этого в init.php
подписываетесь на событие
Bitrix\Main\EventManager::getInstance()->addEventHandler( 'documentgenerator', 'onDriverCollectClasses', static function(\Bitrix\Main\Event $event) { return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, [ 'documentClassName' => MyDocument::class, ]); } );
Теперь Bitrix\DocumentGenerator\Driver::getInstance()->getDocumentClassName()
вернет имя вашего класса.
Статические методы Bitrix\DocumentGenerator\Document::createByTemplate()
и Bitrix\DocumentGenerator\Document::loadById()
вернут объекты именно этого класса.
Классы, которые можно подменить, их имена и классы, которые надо наследовать
[ 'documentClassName' => Bitrix\DocumentGenerator\Document::class, 'templateClassName' => Bitrix\DocumentGenerator\Template::class, 'userPermissionsClassName' => Bitrix\DocumentGenerator\UserPermissions::class, 'dataProviderManager' => Bitrix\DocumentGenerator\DataProviderManager::class, ];
Использование своих парсеров вместо стандартных Docx/DocxXml
Например, вы хотите изменить механизм определения значений полей, вставляемых в размноживаемые блоки.
Надо воспользоваться предыдущим примером, но заменить своего наследника Bitrix\DocumentGenerator\Template
class MyDocxXml extends Bitrix\DocumentGenerator\Body\DocxXml { protected function getValuesForMultiplyingBlock(string $placeholder, Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider $list, $data, array $fieldNames): array { // some custom logic return []; } } class MyDocx extends Bitrix\DocumentGenerator\Body\Docx { protected function getXmlClassName(): string { return MyDocxXml::class; } } class MyTemplate extends Bitrix\DocumentGenerator\Template { public function getBodyClassName(): string { return MyDocx::class; } }
Кроме подмены класса в явном виде, можно подписаться на ORM-событие создания записи в таблице b_documentgenerator_template
и менять в нем содержимое колонки BODY_TYPE
. Класс парсера теперь берется из этой колонки.
Переопределение провайдеров (добавление своих полей)
Изменить значения полей можно с помощью события onBeforeProcessDocument
. А вот с добавлением полей раньше была проблема.
Теперь можно использовать свои провайдеры вместо штатных. Есть два способа:
- Можно переопределить
DataProviderManager
и в нем крутить-вертеть классы-провайдеры и их поля как угодно - Упрощенный вариант (когда нужно все провайдеры одного класса заменить его наследником) - через событие
Задача: добавить в сделку какое-то своё уникальное поле.
Решение: наследуем стандартный провайдер, в наследнике добавляем нужное поле, указываем наследник в качестве замены стандартному провайдеру.
// содержимое файла documentgenerator_classes.php if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) { die(); } if(\Bitrix\Main\Loader::includeModule('crm')) { class MyDeal extends \Bitrix\Crm\Integration\DocumentGenerator\DataProvider\Deal { public function getFields(): array { if($this->fields === null) { parent::getFields(); $this->fields['MY_SUPER_UNIQUE_FIELD'] = [ 'TITLE' => 'Cool Fields', 'VALUE' => [ $this, 'getUniqueFieldValue', ], ]; } return $this->fields; } public function getUniqueFieldValue() { return 'unique_value'; } } } // обработчик события в init.php Bitrix\Main\EventManager::getInstance()->addEventHandler( 'documentgenerator', 'onDataProviderManagerFillSubstitutionProviders', static function(\Bitrix\Main\Event $event) { require_once('documentgenerator_classes.php'); $result = []; if(\Bitrix\Main\Loader::includeModule('crm')) { $result = [ Bitrix\Crm\Integration\DocumentGenerator\DataProvider\Deal::class => MyDeal::class, ]; } return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, $result); } );
Теперь в списке полей будет отображаться это поле и его можно использовать в своих шаблонах.
При этом визуально ничего не изменится, в интерфейсе по-прежнему будут стандартные провайдеры.
У этого метода есть ограничения - заменяемый провайдер должен был наследником оригинального.