Commit | Line | Data |
---|---|---|
fa75f064 TO |
1 | <?php |
2 | namespace Civi\Token; | |
3 | ||
4 | use Civi\Token\Event\TokenRegisterEvent; | |
5 | use Civi\Token\Event\TokenValueEvent; | |
6 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
7 | ||
8 | /** | |
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`. | |
12 | * | |
13 | * @package Civi\Token | |
14 | */ | |
15 | class ImpliedContextSubscriber implements EventSubscriberInterface { | |
16 | ||
17 | public static function getSubscribedEvents() { | |
18 | return [ | |
19 | 'civi.token.list' => ['onRegisterTokens', 1000], | |
20 | 'civi.token.eval' => ['onEvaluateTokens', 1000], | |
21 | ]; | |
22 | } | |
23 | ||
24 | /** | |
25 | * When listing tokens, ensure that implied data is visible. | |
26 | * | |
27 | * Ex: If `$context['participantId']` is part of the schema, then | |
28 | * `$context['eventId']` will also be part of the schema. | |
29 | * | |
30 | * This fires early during the `civi.token.list` process to ensure that | |
31 | * other listeners see the updated schema. | |
32 | * | |
33 | * @param \Civi\Token\Event\TokenRegisterEvent $e | |
34 | */ | |
35 | public function onRegisterTokens(TokenRegisterEvent $e): void { | |
36 | $tokenProc = $e->getTokenProcessor(); | |
37 | foreach ($this->findRelevantMappings($tokenProc) as $mapping) { | |
38 | $tokenProc->addSchema($mapping['destEntityId']); | |
39 | } | |
40 | } | |
41 | ||
42 | /** | |
43 | * When evaluating tokens, ensure that implied data is loaded. | |
44 | * | |
45 | * Ex: If `$context['participantId']` is supplied, then lookup the | |
46 | * corresponding `$context['eventId']`. | |
47 | * | |
48 | * This fires early during the `civi.token.list` process to ensure that | |
49 | * other listeners see the autoloaded values. | |
50 | * | |
51 | * @param \Civi\Token\Event\TokenValueEvent $e | |
52 | */ | |
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; | |
58 | }; | |
59 | ||
60 | $ids = []; | |
61 | foreach ($tokenProc->getRows() as $row) { | |
62 | $ids[] = $getSrcId($row); | |
63 | } | |
64 | $ids = \array_diff(\array_unique($ids), [NULL]); | |
65 | if (empty($ids)) { | |
66 | continue; | |
67 | } | |
68 | ||
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]) | |
73 | ->execute() | |
74 | ->fetchMap('id', $fkColumn); | |
75 | ||
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]); | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | ||
86 | /** | |
87 | * Are there any context-fields for which we should do lookups? | |
88 | * | |
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`. | |
92 | * | |
93 | * @param \Civi\Token\TokenProcessor $tokenProcessor | |
94 | */ | |
95 | private function findRelevantMappings(TokenProcessor $tokenProcessor): iterable { | |
96 | $schema = $tokenProcessor->context['schema']; | |
97 | yield from []; | |
98 | foreach ($this->getMappings() as $mapping) { | |
99 | if (in_array($mapping['srcEntityRec'], $schema) || in_array($mapping['srcEntityId'], $schema)) { | |
100 | yield $mapping; | |
101 | } | |
102 | } | |
103 | } | |
104 | ||
105 | private function getMappings(): iterable { | |
106 | yield [ | |
107 | 'srcEntityId' => 'participantId', | |
108 | 'srcEntityRec' => 'participant', | |
109 | 'fk' => 'civicrm_participant.event_id', | |
110 | 'destEntityId' => 'eventId', | |
111 | ]; | |
112 | } | |
113 | ||
114 | } |