4 use Civi\Token\Event\TokenRegisterEvent
;
5 use Civi\Token\Event\TokenValueEvent
;
6 use Symfony\Component\EventDispatcher\EventSubscriberInterface
;
9 * Suppose you have `$context['participantId']`. You can infer a
10 * corresponding `$context['eventId']`. The ImpliedContextSubscriber reads
11 * values like `participantId` and fills-in values like `eventId`.
15 class ImpliedContextSubscriber
implements EventSubscriberInterface
{
17 public static function getSubscribedEvents() {
19 'civi.token.list' => ['onRegisterTokens', 1000],
20 'civi.token.eval' => ['onEvaluateTokens', 1000],
25 * When listing tokens, ensure that implied data is visible.
27 * Ex: If `$context['participantId']` is part of the schema, then
28 * `$context['eventId']` will also be part of the schema.
30 * This fires early during the `civi.token.list` process to ensure that
31 * other listeners see the updated schema.
33 * @param \Civi\Token\Event\TokenRegisterEvent $e
35 public function onRegisterTokens(TokenRegisterEvent
$e): void
{
36 $tokenProc = $e->getTokenProcessor();
37 foreach ($this->findRelevantMappings($tokenProc) as $mapping) {
38 $tokenProc->addSchema($mapping['destEntityId']);
43 * When evaluating tokens, ensure that implied data is loaded.
45 * Ex: If `$context['participantId']` is supplied, then lookup the
46 * corresponding `$context['eventId']`.
48 * This fires early during the `civi.token.list` process to ensure that
49 * other listeners see the autoloaded values.
51 * @param \Civi\Token\Event\TokenValueEvent $e
53 public function onEvaluateTokens(TokenValueEvent
$e): void
{
54 $tokenProc = $e->getTokenProcessor();
55 foreach ($this->findRelevantMappings($tokenProc) as $mapping) {
56 $getSrcId = function($row) use ($mapping) {
57 return $row->context
[$mapping['srcEntityId']] ??
$row->context
[$mapping['srcEntityRec']]['id'] ??
NULL;
61 foreach ($tokenProc->getRows() as $row) {
62 $ids[] = $getSrcId($row);
64 $ids = \array_diff
(\array_unique
($ids), [NULL]);
69 [$srcTable, $fkColumn] = explode('.', $mapping['fk']);
70 $fks = \CRM_Utils_SQL_Select
::from($srcTable)
71 ->where('id in (#ids)', ['ids' => $ids])
72 ->select(['id', $fkColumn])
74 ->fetchMap('id', $fkColumn);
76 $tokenProc->addSchema($mapping['destEntityId']);
77 foreach ($tokenProc->getRows() as $row) {
78 $srcId = $getSrcId($row);
79 if ($srcId && empty($row->context
[$mapping['destEntityId']])) {
80 $row->context($mapping['destEntityId'], $fks[$srcId]);
87 * Are there any context-fields for which we should do lookups?
89 * Ex: If the `$tokenProcessor` has the `participantId`s, then we would want
90 * to know any rules that involve `participantId`. But we don't need to know
91 * rules that involve `contributionId`.
93 * @param \Civi\Token\TokenProcessor $tokenProcessor
95 private function findRelevantMappings(TokenProcessor
$tokenProcessor): iterable
{
96 $schema = $tokenProcessor->context
['schema'];
98 foreach ($this->getMappings() as $mapping) {
99 if (in_array($mapping['srcEntityRec'], $schema) ||
in_array($mapping['srcEntityId'], $schema)) {
105 private function getMappings(): iterable
{
107 'srcEntityId' => 'participantId',
108 'srcEntityRec' => 'participant',
109 'fk' => 'civicrm_participant.event_id',
110 'destEntityId' => 'eventId',