X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;ds=sidebyside;f=Civi%2FToken%2FTokenProcessor.php;h=e8ee1b8f73f5d65f8d331284ac67eff50b168512;hb=587db57945b9654fceb44d36b55acea5c15281de;hp=4fa03b9038fb084e84b825834f17994ca30b84e1;hpb=372812c8940e9e2e804ec0e0439171432e1f3c2b;p=civicrm-core.git diff --git a/Civi/Token/TokenProcessor.php b/Civi/Token/TokenProcessor.php index 4fa03b9038..e8ee1b8f73 100644 --- a/Civi/Token/TokenProcessor.php +++ b/Civi/Token/TokenProcessor.php @@ -6,6 +6,37 @@ use Civi\Token\Event\TokenRenderEvent; use Civi\Token\Event\TokenValueEvent; 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 { /** @@ -27,7 +58,7 @@ class TokenProcessor { * - schema: array, a list of fields that will be provided for each row. * This is automatically populated with any general context * keys, but you may need to add extra keys for token-row data. - * ex: ['contactId', 'activity_id']. (Note we are standardising on the latter). + * ex: ['contactId', 'activityId']. */ public $context; @@ -115,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] = [ @@ -125,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; } /** @@ -157,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 = []; @@ -170,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)); @@ -187,7 +264,7 @@ class TokenProcessor { * * @param string $field * Ex: 'contactId'. - * @param $subfield + * @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; } @@ -294,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; }