Token Reference
Introduction¶
CiviCRM tokens enable one to generate personalized emails, reminders, print documents. etc.
When working with tokens as a developer, there are a few major tasks:
- Composing a message by evaluating a token expression (
Hello {contact.first_name}! How do you like {address.city}?
). - Defining a token and its content.
- Listing available tokens.
For each task, there are a couple available patterns, and we'll explore them in more depth below. But first, it helps to see a few examples.
Examples¶
Token | Description | Example Value |
---|---|---|
{domain.name} |
Name of this domain (organization/site/deployment) | Do-Gooder International |
{domain.address} |
Meta-token with the full-formed mailing address of this domain (organization/site/deployment)) | 100 Roadie St Townville 54321 |
{contact.first_name} |
The contact's first_name |
Alice |
{contact.display_name} |
The contact's display_name |
Bob Roberts |
{mailing.name} |
The name of the mailing | January Newsletter |
For more examples of tokens and token replacement, see User Guide: Common workflows: Tokens and mail merge.
Composing messages¶
Individual messaging¶
TokenSmarty
was added in v5.41. (History)
The TokenSmarty
utility provides a simple way to render one message:
// Usage
$rendered = CRM_Core_TokenSmarty::render(array $templates, array $context);
// Example
$rendered = CRM_Core_TokenSmarty::render(
['html' => '<p>Hello {contact.display_name}!</p>'],
['contactId' => 123],
);
echo $rendered['html'];
This example shows a few important pieces:
- The
$templates
array provides one (or more) templates. Templates should be given astext
orhtml
. If you are composing an email message (which may have both text and HTML components), then you may provide multiple templates. - The
$context
array provides data. As you add more context data, it will enable more tokens. (For example, passingcontactId
will enable{contact.*}
tokens.) For more detailed information, see Appendix: Token Context. - In CiviCRM, it is common for user-supplied templates to be written with a mix of token and Smarty notations.
The TokenSmarty
utility is suited to rendering one message for one recipient. It is a small wrapper for TokenProcessor
, which provides
additional functionality - such as batch-processing and token-inspection.
Batched messaging¶
TokenProcessor
was added in v4.7 and phased-in gradually, with overall adoption around v5.43. (History)
If you will be composing a large number of concurrent messages (eg an email-blast for thousands of recipients), then you should use
TokenProcessor
directly. TokenProcessor
includes optimizations for handling batched data.
To compose a batch, create an instance of TokenProcessor
, fill-in details about the batch, and then evaluate the tokens. For example:
use Civi\Token\TokenProcessor;
$tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
// Give a unique identifier for this instance.
'controller' => __CLASS__,
// Enable or disable handling of Smarty notation.
'smarty' => FALSE,
// List any data fields that we plan to provide.
'schema' => ['contactId'],
]);
// Define the message template(s).
$tokenProcessor->addMessage('body_text', 'Hello {contact.display_name}!', 'text/plain');
$tokenProcessor->addMessage('body_html', '<p>Hello {contact.display_name}!</p>', 'text/html');
// Fill in data about the recipients/targets.
$tokenProcessor->addRow(['contactId' => 123]);
$tokenProcessor->addRow(['contactId' => 456]);
$tokenProcessor->addRow(['contactId' => 789]);
// Evaluate any tokens which are referenced in the message.
// In this example, `evaluate()` will:
// - Recognize that `{contact.display_name}` needs to be available.
// - Fetch the display names (`SELECT id, display_name FROM civicrm_contact WHERE id IN (123,456,789)`)
$tokenProcessor->evaluate();
// Display mail-merge data.
foreach ($tokenProcessor->getRows() as $row) {
echo $row->render('body_text');
}
This example is similar to the TokenSmarty
example, but it goes a bit further and provides more structure:
- It handles several recipents with different context-data (
addRow(array $context)
). - It distinguishes between per-recipient context-data (
addRow(array $context)
) and shared context-data (new TokenProcesser($dispatcher, array $context)
). - It handles several message templates with distinct names and MIME types (
addMessage(string $name, string $template, string $type)
). - It splits composition into a couple steps:
evaluate()
examines the tokens and loads supplemental data.render()
gives you the final text.
Defining tokens¶
To define new tokens, you must subscribe to the token events. Depending on how much legacy support you need to provide you may use the preferred ones, or the legacy ones:
Events | Strength | Weakness |
---|---|---|
civi.token.list +civi.token.eval |
More supported.More forward compatibility. More complete API. Works with any entity. |
Less backward compatibilty. |
hook_civicrm_tokens + hook_civicrm_tokenValues |
More backward compatiblity. | Deprecated.Contact -only. Less complete API. More DB queries |
Let us do an example. This listens to civi.token.*
and defines two tokens, {profile.viewUrl}
and {profile.viewLink}
.
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* Add token services to the container.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
*/
function example_civicrm_container(ContainerBuilder $container) {
$container->addResource(new FileResource(__FILE__));
$container->findDefinition('dispatcher')->addMethodCall('addListener',
['civi.token.list', 'example_register_tokens']
)->setPublic(TRUE);
$container->findDefinition('dispatcher')->addMethodCall('addListener',
['civi.token.eval', 'example_evaluate_tokens']
)->setPublic(TRUE);
}
function example_register_tokens(\Civi\Token\Event\TokenRegisterEvent $e) {
$e->entity('profile')
->register('viewUrl', ts('Profile View URL'))
->register('viewLink', ts('Profile View Link'));
}
function example_evaluate_tokens(\Civi\Token\Event\TokenValueEvent $e) {
foreach ($e->getRows() as $row) {
/** @var TokenRow $row */
$row->format('text/html');
$row->tokens('profile', 'viewUrl', 'http://example.com/profile/' . $row->context['contactId']);
$row->tokens('profile', 'viewLink', ts("<a href='%1'>Open Profile</a>", array(
1 => $row->tokens['profile']['viewUrl'],
)));
}
}
Some notes on the the above:
$row->context['...']
returns contextual data, regardless of whether you declared it at the row level or the processor level.- To update a row's data, use the
context()
andtokens()
methods. To read a row's data, use the $context and $tokens properties. These interfaces support several notations, which are described in theTokenRow
class. - You have control over the loop. You can do individual data-lookups in the loop (for simplicity) – or you can also do prefetches and batched lookups (for performance).
- To avoid unnecessary computation, you can get a list of tokens which are actually required by this mailing. Call
$e->getTokenProcessor()->getMessageTokens()
. - In this example, we defined tokens in HTML format, and we rely on a default behavior that auto-converts between HTML and text (as needed). However, we could explicitly define HTML and plain-text variants by calling
$row->format()
and$row->tokens()
again. - The class
\Civi\Token\AbstractTokenSubscriber
provides a more structured/opinionated way to handle these events. - For background on the
event dispatcher
(e.g.listeners
vs subscribers), see Symfony Documentation
The main limitation of this technique is that it only works with TokenProcessor
. At time of writing, this is used for Scheduled Reminders and FlexMailer.
Listing tokens¶
Suppose the application sends an automated "Thank You" letter for each donation. The administrator may customize this letter - choosing their own prose and mixing in different tokens. But which tokens can they use? In the administrative interface, you should provide a list of available tokens (via dropdown, autocomplete, etc).
The list of tokens depends on the use-case and available data, as in:
Use-Case | Available Data | Available Tokens |
---|---|---|
Send a donation "Thank You" letter | Contact recordContribution record |
{contact.*} {contribution.*} |
Send a newsletter | Contact recordMailing record |
{contact.*} {mailing.*} {action.*} |
This list is also influenced by extensions - an extension may define extra tokens like {contact.foo_bar}
or {foo.bar}
.
An application may request the list of available tokens via listTokens()
:
$tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
'controller' => __CLASS__,
'smarty' => FALSE,
'schema' => ['contactId', 'contributionId'],
]);
$available = $tokenProcessor->listTokens();
Note that we do not provide a literal contactId
or contributionId
-- these values do not exist yet. (We are acting as administrators preparing a
template.) However, we will provide them when it matters - and this means various tokens will be valid. The schema
gives an important hint to
determine available tokens.
Appendix: Token Context¶
When creating a TokenProcessor
and adding rows/recipients, one passes in context data. The data passed in will determine which tokens
are available. These are common parameters for enabling common tokens:
Parameter | Description |
---|---|
activityId (int ) |
Required for {activity.*} tokens |
contactId (int ) |
Required for {contact.*} tokens |
contributionId (int ) |
Required for {contribution.*} tokens |
mailingId (int ) |
Required for {mailing.*} tokens |
mailingActionTarget (array) |
Required for CiviMail {action.*} tokens |
mailingJobId (int ) |
Required for CiviMail {action.*} tokens |
Additionally, there are some generic parameters. These provide extra hints to the token-providers about what's going on.
Parameter | Description |
---|---|
controller (string ) |
Give a symbolic name for this processor, such as "CiviMail" or "ScheduledReminder". Usually, this is the name of the controller class. |
smarty (bool ) |
Enable or disable support for Smarty directives |
schema (string[] ) |
List of fields used by the TokenProcessor. By default, this is array_keys($context) . However, you may add extra fields here to promise that values will be provided in the future. See Listing Tokens. |
Appendix: History¶
Token functionality originated in CiviMail, which focuses on writing and delivering newsletters to large constituencies. In its original form, the design placed heavy weight on:
- Performance: Divide mail-composition into batches and split the batches among parallel workers. Moreover, when processing each batch, improve efficiency by minimizing the #SQL queries - i.e. fetch all records in a batch at once, and only fetch columns which are actually used.
- Security: Do not trust email authors with a fully programmable language.
- Contact Records: The main data for mail-merge came from contact records. Other data (contribution, event, participant, membership, etc) were not applicable.
- Adaptive Text/HTML: Email messages often have two renditions,
text/plain
andtext/html
. Some tokens, such as{domain.address}
, may present different formatting in each medium. Other tokens, such as{action.unsubscribe}
, can even present a different user-experience.
Over time, the token functionality evolved:
- Add optional support for more powerful templates with conditions and loops (Smarty). (In the case of CiviMail, this was still disabled by default as a security consideration, but in other use-cases it might be enabled by default.)
- Add a hook for custom tokens.
- Expand to other applications, such as individual mailings, print letters, receipts for contributions, and scheduled reminders.
Up through CiviCRM v4.7, the primary interfaces for working with tokens were CRM_Utils_Token (helpers for composing messages), hook_civicrm_tokens and hook_civicrm_tokenValues (helpers for custom tokens).
CiviCRM v4.7 introduced Civi\Token\TokenProcessor
and civi.token.*
, which provide a more flexible way to define and process tokens. These preserve the performance, batching, and security virtues of CRM_Utils_Token
and also:
- Allow more contextual information -- enabling tokens for more entities (
Contribution
,Participant
, etc). - Loosen the coupling between token-consumers and token-providers.
- Loosen the coupling between token-content and template-language.
- Centralize the evaluation of token language.
- Distinguish token-data which is defined with (or without) HTML content.
- Further reduce unnecessary computations (custom tokens only need to be prcoessed if they are being used)
In v4.7, the TokenProcessor
and civi.token.*
were initially used for Scheduled Reminders (CRM-13244), and they could be
optionally used for CiviMail (by installing/configuring FlexMailer). Specific use-case have changed during 5.x, with most
being converted circa v5.43.
Appendix: CRM_Utils_Token¶
For most of its history, CiviMail used a helper class, CRM_Utils_Token
, with a number of static helper functions. This class is now deprecated and should not be used.