Documentation

Customization

Adding provider and data

Adding your own provider to list

Information on how to create your own provider can be found here.

Register the handler for getting list of providers:

//For module
\Bitrix\Main\EventManager::getInstance(
)->registerEventHandler('documentgenerator', 'onGetDataProviderList', 'crm', '\Bitrix\Crm\Integration\DocumentGeneratorManager', 'getDataProviders');
//runtime
\Bitrix\Main\EventManager::getInstance(
)->addEventHandler('documentgenerator', 'onGetDataProviderList', ['myClass', 'addDocumentProviders']);

The handler must return an array type:

$result[$className] = [
    'NAME' => 'Language name',
    'CLASS' => $className,
    'MODULE' => 'myModule',
];

where $className - full name of class with namespace.

It must be descendant of class \Bitrix\DocumentGenerator\DataProvider.

Adding additional data into the result \Bitrix\DocumentGenerator\Document::getFile()

Define the method getAdditionalDocumentInfo() in your class descendant \Bitrix\DocumentGenerator\DataProvider.

This method must return an array, included into a result of \Bitrix\DocumentGenerator\Document::getFile().

Modifying field values

Updating field values before generating a document (from version documentgenerator 18.0.6)

Subscribe to event:

\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onBeforeProcessDocument', 'onBeforeProcessDocument');

function onBeforeProcessDocument($event)
{
	$document = $event->getParameter('document');
	/** @var \Bitrix\DocumentGenerator\Document $document */
	// add additional field descriptions
	// $document->setFields($newFields);
	// add field values
	//$document->setValues(['someField' => 'myCustomValue']);
	// get list of fields and their current values
    //$fields = $document->getFields();
}

Event handler may no return, all actions are performed with document object.

This object must be handled with extra caution. Calling $document->getFile() in the handler may result in an infinite recursion.

Modifying lists

Modifying list content (from version documentgenerator 20.0.0)

Previously, reflection had to be used for modifying lists in the event onBeforeProcessDocument.

Now several new useful methods have become available:

Method Description
ArrayDataProvider::getItemByIndex(int $index)Returns a list item by index (if available).
ArrayDataProvider::replaceItem(int $index, $item)Changes item list with index $index to $item.

When there is no list item with this index, throws the exception OutOfRangeException.

ArrayDataProvider::addItem($item): intAdds new item list to the end of list. Returns new item index.
ArrayDataProvider::deleteItemByIndex(int $index)Deletes item by the index $index, and then all items will be shifted.
HashDataProvider::getData()x)Returns values of all provider fields.
HashDataProvider::setData(array $data)Completely rewrites field values.

Below is an example of how new methods can be used to remove all products, except the first one. And to change the name for the last one

\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);
                }
			}
		}
	}
);

Tracking document updates

Tracking document updates

Subscribe to events

// create document (after saving entry to databse)
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onCreateDocument', ['myClass', 'onCreateDocument']);
// update document
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onUpdateDocument', ['myClass', 'onUpdateDocument']);

In both cases, similar to onBeforeProcessDocument, the event will receive the parameter $document with document object.

// completing document conversion
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onDocumentTransformationComplete', ['myClass', 'onDocumentTransformationComplete']);

Two parameters are received in this event handler - $documentId и $data.

$documentId - document ID. Its document can be invoked using \Bitrix\DocumentGenerator\Document::loadById($documentId)

$data - result \Bitrix\DocumentGenerator\Document::getFile().

Custom identifier and storage

Implementing custom modifiers for a template

To process own providers, write the descendant \Bitrix\DocumentGenerator\Value.

Provider field returns generated object. Otherwise, indicate default type and format in the field description.

Its recommended to proceed by following an example (such as \Bitrix\DocumentGenerator\Value\DateTime).

Using own custom document storage

First, you must have a class to provide an interface \Bitrix\DocumentGenerator\Storage. After that, you can either use the event onBeforeProcessDocument to change document storages before processing it:

$storage = new MyStorage();
$document->setStorage($storage);

or save storage value by default via the option

\Bitrix\Main\Config\Option::set('documentgenerator', 'default_storage_type', MyStorage::class);

Before proceeding with this, its recommended to view existing classes \Bitrix\DocumentGenerator\Storage\File, \Bitrix\DocumentGenerator\Storage\BFile, \Bitrix\DocumentGenerator\Storage\Disk.


Tracking the views

Tracking views of public link to document (from documentgenerator 18.6.0)

PHP event sending when viewing public link

\Bitrix\Main\EventManager::getInstance()->send(new \Bitrix\Main\Event('documentgenerator', 'onPublicView', ['document' => $document]));

where $document - object

Retrieve document template to find out, which module's document was used

$template = $document->getTemplate();
if($template && $template->MODULE_ID == 'crm')
{
    // your code for crm here
}

When you need to trace document for CRM and retrieve data on document source, you can use the following code:

$provider = $document->getProvider();
if($provider && $provider instanceof \Bitrix\Crm\Integration\DocumentGenerator\DataProvider\CrmEntityDataProvider)
{
    $ownerId = $provider->getSource();
    $ownerType = $provider->getCrmOwnerType();
}

New updates since version 20.0.0

Redefined logic

The module allows using custom classes instead of pre-installed ones. This way, module behaviour can be modified.

To do that, subscribe to the event onDriverCollectClasses of module documentgenerator.

Array is received as the result of event, where the key is the class name and value - your descendant.

For example, you want to use your own implementation of Document to use your own algorithm for generating numbers instead of in-built numerator.

Create you own descendant to do that:

class MyDocument extends Bitrix\DocumentGenerator\Document 
{
	public function getNumber(bool $preview = true): string
    {
        return 'my_super_smart_number';
    }
}

After that, subscribe to the event in 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,
		]);
	}
);

Now Bitrix\DocumentGenerator\Driver::getInstance()->getDocumentClassName() returns the name of your class.

Static methods Bitrix\DocumentGenerator\Document::createByTemplate() and Bitrix\DocumentGenerator\Document::loadById() return objects of this specific class.

Classes that can be substituted, their names and classes to be inherited

[
    'documentClassName' => Bitrix\DocumentGenerator\Document::class,
    'templateClassName' => Bitrix\DocumentGenerator\Template::class,
    'userPermissionsClassName' => Bitrix\DocumentGenerator\UserPermissions::class,
    'dataProviderManager' => Bitrix\DocumentGenerator\DataProviderManager::class,
];

Using your own parsers instead of standard Docx/DocxXml

For example, you want to change mechanism for determining field values, inserted into multiplied blocks.

Use the previous example, but replace the descendant 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;    
    } 
}

Additionally to replacing class directly, you can subscribe to ORM event for creating an entry in table b_documentgenerator_template and modify content in column BODY_TYPE. Now the parser class is sourced from this column.

Redefining providers (adding your own custom fields)

Modifying field values can be done via the event onBeforeProcessDocument. However, adding fields encountered some issues in the past.

That's why now you can use your own providers instead of standard ones. There are two ways to do it:

  1. You can redefine DataProviderManager and to handle provider classes and their fields in different ways in it
  2. Simplified variant (when all providers of the same class must be replaced by its descendant) - via an event

Objective: add to a deal your own unique field.

Solution: inherit standard provider, add a required field in the descendant and specify the descendant as replacement to standard provider.

// file content 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';
		}
	}
}

// event handler in 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);
	}
);

Now the list of fields will show this field and it can be used in your templates.

With this, nothing will be changed visually, the interface will still have standard providers available.

This method has limitations: replaced provider must be a descendant to the original method.



© «Bitrix24», 2001-2024