Rework the listeners so that they are called on the basis of each entitiy rather...
[civicrm-core.git] / ext / afform / core / Civi / Api4 / Action / Afform / Submit.php
1 <?php
2
3 namespace Civi\Api4\Action\Afform;
4
5 use Civi\Afform\Event\AfformSubmitEvent;
6
7 /**
8 * Class Submit
9 * @package Civi\Api4\Action\Afform
10 */
11 class Submit extends AbstractProcessor {
12
13 const EVENT_NAME = 'civi.afform.submit';
14
15 /**
16 * Submitted values
17 * @var array
18 * @required
19 */
20 protected $values;
21
22 protected function processForm() {
23 $entityValues = $entityIds = [];
24 foreach ($this->_formDataModel->getEntities() as $entityName => $entity) {
25 $entityIds[$entityName] = NULL;
26 foreach ($this->values[$entityName] ?? [] as $values) {
27 $entityValues[$entity['type']][$entityName][] = $values + ['fields' => []];
28 // Predetermined values override submitted values
29 if (!empty($entity['data'])) {
30 foreach ($entityValues[$entity['type']][$entityName] as $index => $vals) {
31 $entityValues[$entity['type']][$entityName][$index]['fields'] = $entity['data'] + $vals['fields'];
32 }
33 }
34 }
35 }
36 $entityWeights = \CRM_Afform_Utils::getEntityWeights($this->_formDataModel->getEntities(), $entityValues);
37 foreach ($entityWeights as $entity => $weight) {
38 $values = $entityValues[$entityMapping[$entity]][$entity];
39 $event = new AfformSubmitEvent($this->_afform, $this->_formDataModel, $this, $values, $entityMapping[$entity], $entity, $entityIds);
40 \Civi::dispatcher()->dispatch(self::EVENT_NAME, $event);
41 }
42 foreach ($event->entityValues as $entityType => $entities) {
43 if (!empty($entities)) {
44 throw new \API_Exception(sprintf("Failed to process entities (type=%s; name=%s)", $entityType, implode(',', array_keys($entities))));
45 }
46 }
47
48 // What should I return?
49 return [];
50 }
51
52 /**
53 * @param \Civi\Afform\Event\AfformSubmitEvent $event
54 * @throws \API_Exception
55 * @see afform_civicrm_config
56 */
57 public static function processContacts(AfformSubmitEvent $event) {
58 if ($event->entityType !== 'Contact') {
59 return;
60 }
61 $entityName = $event->entityName;
62 $api4 = $event->formDataModel->getSecureApi4($entityName);
63 $saved = $api4('Contact', 'save', ['records' => [$event->values['fields']]])->first();
64 $event->entityIds[$entityName] = $saved['id'];
65 self::saveJoins('Contact', $saved['id'], $event->values['joins'] ?? []);
66 }
67
68 /**
69 * @param \Civi\Afform\Event\AfformSubmitEvent $event
70 * @throws \API_Exception
71 * @see afform_civicrm_config
72 */
73 public static function processGenericEntity(AfformSubmitEvent $event) {
74 if ($event->entityType === 'Contact') {
75 return;
76 }
77 $entityName = $event->entityName;
78 $api4 = $event->formDataModel->getSecureApi4($event->entityName);
79 // Replace Entity reference fields that reference other form based entities with their created ids.
80 foreach ($record['fields'] as $field => $value) {
81 if (array_key_exists($value, $event->entityIds) && !empty($event->entityIds[$value])) {
82 $record['fields'][$field] = $event->entityIds[$value];
83 }
84 }
85 $saved = $api4($entityType, 'save', ['records' => [$event->values['fields']]])->first();
86 $event->entityIds[$entityName] = $saved['id'];
87 self::saveJoins($entityType, $saved['id'], $event->values['joins'] ?? []);
88 }
89
90 protected static function saveJoins($mainEntityName, $entityId, $joins) {
91 foreach ($joins as $joinEntityName => $join) {
92 $values = self::filterEmptyJoins($joinEntityName, $join);
93 // TODO: REPLACE works for creating or updating contacts, but different logic would be needed if
94 // the contact was being auto-updated via a dedupe rule; in that case we would not want to
95 // delete any existing records.
96 if ($values) {
97 civicrm_api4($joinEntityName, 'replace', [
98 // Disable permission checks because the main entity has already been vetted
99 'checkPermissions' => FALSE,
100 'where' => self::getJoinWhereClause($mainEntityName, $joinEntityName, $entityId),
101 'records' => $values,
102 ]);
103 }
104 // REPLACE doesn't work if there are no records, have to use DELETE
105 else {
106 try {
107 civicrm_api4($joinEntityName, 'delete', [
108 // Disable permission checks because the main entity has already been vetted
109 'checkPermissions' => FALSE,
110 'where' => self::getJoinWhereClause($mainEntityName, $joinEntityName, $entityId),
111 ]);
112 }
113 catch (\API_Exception $e) {
114 // No records to delete
115 }
116 }
117 }
118 }
119
120 /**
121 * Filter out joins that have been left blank on the form
122 *
123 * @param $entity
124 * @param $join
125 * @return array
126 */
127 private static function filterEmptyJoins($entity, $join) {
128 return array_filter($join, function($item) use($entity) {
129 switch ($entity) {
130 case 'Email':
131 return !empty($item['email']);
132
133 case 'Phone':
134 return !empty($item['phone']);
135
136 case 'IM':
137 return !empty($item['name']);
138
139 case 'Website':
140 return !empty($item['url']);
141
142 default:
143 \CRM_Utils_Array::remove($item, 'id', 'is_primary', 'location_type_id', 'entity_id', 'contact_id', 'entity_table');
144 return (bool) array_filter($item);
145 }
146 });
147 }
148
149 }