X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=Civi%2FToken%2FTokenProcessor.php;h=e8ee1b8f73f5d65f8d331284ac67eff50b168512;hb=5b394c8f531cb30eccf5507bf7c08997603bf217;hp=c989773435462bca6ed891509dc343de7a406d87;hpb=f6af7e6f06447f2725f5878bb824b8e792d3cc30;p=civicrm-core.git diff --git a/Civi/Token/TokenProcessor.php b/Civi/Token/TokenProcessor.php index c989773435..e8ee1b8f73 100644 --- a/Civi/Token/TokenProcessor.php +++ b/Civi/Token/TokenProcessor.php @@ -4,9 +4,39 @@ namespace Civi\Token; use Civi\Token\Event\TokenRegisterEvent; use Civi\Token\Event\TokenRenderEvent; use Civi\Token\Event\TokenValueEvent; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Traversable; +/** + * The TokenProcessor is a template/token-engine. It is heavily influenced by + * traditional expectations of CiviMail, but it's adapted to an object-oriented, + * extensible design. + * + * BACKGROUND + * + * The CiviMail heritage gives the following expectations: + * + * - Messages are often composed of multiple parts (e.g. HTML-part, text-part, and subject-part). + * - Messages are often composed in batches for multiple recipients. + * - Tokens are denoted as `{foo.bar}`. + * - Data should be loaded in an optimized fashion - fetch only the needed + * columns, and fetch them with one query (per-table). + * + * The question of "optimized" data-loading is a key differentiator/complication. + * This requires some kind of communication/integration between the template-parser and data-loader. + * + * USAGE + * + * There are generally two perspectives on using TokenProcessor: + * + * 1. Composing messages: You need to specify the template contents (eg `addMessage(...)`) + * and the recipients' key data (eg `addRow(['contact_id' => 123])`). + * 2. Defining tokens/entities/data-loaders: You need to listen for TokenProcessor + * events; if any of your tokens/entities are used, then load the batch of data. + * + * Each use-case is presented with examples in the Developer Guide: + * + * @link https://docs.civicrm.org/dev/en/latest/framework/token/ + */ class TokenProcessor { /** @@ -33,7 +63,7 @@ class TokenProcessor { public $context; /** - * @var EventDispatcherInterface + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $dispatcher; @@ -82,7 +112,7 @@ class TokenProcessor { protected $next = 0; /** - * @param EventDispatcherInterface $dispatcher + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher * @param array $context */ public function __construct($dispatcher, $context) { @@ -116,9 +146,12 @@ class TokenProcessor { /** * Add a row of data. * + * @param array|NULL $context + * Optionally, initialize the context for this row. + * Ex: ['contact_id' => 123]. * @return TokenRow */ - public function addRow() { + public function addRow($context = NULL) { $key = $this->next++; $this->rowContexts[$key] = []; $this->rowValues[$key] = [ @@ -126,7 +159,29 @@ class TokenProcessor { 'text/html' => [], ]; - return new TokenRow($this, $key); + $row = new TokenRow($this, $key); + if ($context !== NULL) { + $row->context($context); + } + return $row; + } + + /** + * Add several rows. + * + * @param array $contexts + * List of rows to add. + * Ex: [['contact_id'=>123], ['contact_id'=>456]] + * @return TokenRow[] + * List of row objects + */ + public function addRows($contexts) { + $rows = []; + foreach ($contexts as $context) { + $row = $this->addRow($context); + $rows[$row->tokenRow] = $row; + } + return $rows; } /** @@ -158,6 +213,11 @@ class TokenProcessor { * Get a list of all tokens used in registered messages. * * @return array + * The list of activated tokens, indexed by object/entity. + * Array(string $entityName => string[] $fieldNames) + * + * Ex: If a message says 'Hello {contact.first_name} {contact.last_name}!', + * then $result['contact'] would be ['first_name', 'last_name']. */ public function getMessageTokens() { $tokens = []; @@ -171,12 +231,28 @@ class TokenProcessor { return $tokens; } + /** + * Get a specific row (i.e. target or recipient). + * + * Ex: echo $p->getRow(2)->context['contact_id']; + * Ex: $p->getRow(3)->token('profile', 'viewUrl', 'http://example.com/profile?cid=3'); + * + * @param int $key + * The row ID + * @return \Civi\Token\TokenRow + * The row is presented with a fluent, OOP facade. + * @see TokenRow + */ public function getRow($key) { return new TokenRow($this, $key); } /** + * Get the list of rows (i.e. targets/recipients to generate). + * + * @see TokenRow * @return \Traversable + * Each row is presented with a fluent, OOP facade. */ public function getRows() { return new TokenRowIterator($this, new \ArrayIterator($this->rowContexts)); @@ -188,6 +264,7 @@ class TokenProcessor { * * @param string $field * Ex: 'contactId'. + * @param string|NULL $subfield * @return array * Ex: [12, 34, 56]. */ @@ -223,13 +300,13 @@ class TokenProcessor { * Get the list of available tokens. * * @return array - * Ex: $tokens['event'] = array('location', 'start_date', 'end_date'). + * Ex: $tokens['event'] = ['location', 'start_date', 'end_date']. */ public function getTokens() { if ($this->tokens === NULL) { $this->tokens = []; $event = new TokenRegisterEvent($this, ['entity' => 'undefined']); - $this->dispatcher->dispatch(Events::TOKEN_REGISTER, $event); + $this->dispatcher->dispatch('civi.token.list', $event); } return $this->tokens; } @@ -238,7 +315,7 @@ class TokenProcessor { * Get the list of available tokens, formatted for display * * @return array - * Ex: $tokens[ '{token.name}' ] = "Token label" + * Ex: $tokens['{token.name}'] = "Token label" */ public function listTokens() { if ($this->listTokens === NULL) { @@ -255,7 +332,7 @@ class TokenProcessor { */ public function evaluate() { $event = new TokenValueEvent($this); - $this->dispatcher->dispatch(Events::TOKEN_EVALUATE, $event); + $this->dispatcher->dispatch('civi.token.eval', $event); return $this; } @@ -278,7 +355,9 @@ class TokenProcessor { $row->fill($message['format']); $useSmarty = !empty($row->context['smarty']); - // FIXME preg_callback. + /** + *@FIXME preg_callback. + */ $tokens = $this->rowValues[$row->tokenRow][$message['format']]; $flatTokens = []; \CRM_Utils_Array::flatten($tokens, $flatTokens, '', '.'); @@ -292,7 +371,7 @@ class TokenProcessor { $event->context = $row->context; $event->row = $row; $event->string = strtr($message['string'], $filteredTokens); - $this->dispatcher->dispatch(Events::TOKEN_RENDER, $event); + $this->dispatcher->dispatch('civi.token.render', $event); return $event->string; } @@ -304,10 +383,11 @@ class TokenRowIterator extends \IteratorIterator { /** * @param TokenProcessor $tokenProcessor - * @param Traversable $iterator + * @param \Traversable $iterator */ public function __construct(TokenProcessor $tokenProcessor, Traversable $iterator) { - parent::__construct($iterator); // TODO: Change the autogenerated stub + // TODO: Change the autogenerated stub + parent::__construct($iterator); $this->tokenProcessor = $tokenProcessor; }