Merge pull request #17141 from mlutfy/reportOutput
[civicrm-core.git] / Civi / Api4 / Generic / Traits / DAOActionTrait.php
CommitLineData
19b53e5b 1<?php
380f3545
TO
2
3/*
4 +--------------------------------------------------------------------+
41498ac5 5 | Copyright CiviCRM LLC. All rights reserved. |
380f3545 6 | |
41498ac5
TO
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
380f3545
TO
10 +--------------------------------------------------------------------+
11 */
12
13/**
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
380f3545
TO
17 * $Id$
18 *
19 */
20
19b53e5b
C
21namespace Civi\Api4\Generic\Traits;
22
19b53e5b 23use Civi\Api4\Utils\FormattingUtil;
19b53e5b
C
24
25/**
26 * @method string getLanguage()
27 * @method setLanguage(string $language)
28 */
29trait DAOActionTrait {
30
31 /**
32 * Specify the language to use if this is a multi-lingual environment.
33 *
34 * E.g. "en_US" or "fr_CA"
35 *
36 * @var string
37 */
38 protected $language;
39
40 /**
41 * @return \CRM_Core_DAO|string
42 */
43 protected function getBaoName() {
44 require_once 'api/v3/utils.php';
45 return \_civicrm_api3_get_BAO($this->getEntityName());
46 }
47
48 /**
a4c7afc0 49 * Convert saved object to array
19b53e5b 50 *
a4c7afc0
CW
51 * Used by create, update & save actions
52 *
53 * @param \CRM_Core_DAO $bao
54 * @param array $input
19b53e5b
C
55 * @return array
56 */
a4c7afc0
CW
57 public function baoToArray($bao, $input) {
58 $allFields = array_column($bao->fields(), 'name');
59 if (!empty($this->reload)) {
60 $inputFields = $allFields;
61 $bao->find(TRUE);
62 }
63 else {
64 $inputFields = array_keys($input);
65 // Convert 'null' input to true null
66 foreach ($input as $key => $val) {
67 if ($val === 'null') {
68 $bao->$key = NULL;
69 }
70 }
71 }
19b53e5b 72 $values = [];
a4c7afc0
CW
73 foreach ($allFields as $field) {
74 if (isset($bao->$field) || in_array($field, $inputFields)) {
75 $values[$field] = $bao->$field ?? NULL;
19b53e5b
C
76 }
77 }
78 return $values;
79 }
80
19b53e5b
C
81 /**
82 * Fill field defaults which were declared by the api.
83 *
84 * Note: default values from core are ignored because the BAO or database layer will supply them.
85 *
86 * @param array $params
87 */
88 protected function fillDefaults(&$params) {
89 $fields = $this->entityFields();
90 $bao = $this->getBaoName();
91 $coreFields = array_column($bao::fields(), NULL, 'name');
92
93 foreach ($fields as $name => $field) {
94 // If a default value in the api field is different than in core, the api should override it.
95 if (!isset($params[$name]) && !empty($field['default_value']) && $field['default_value'] != \CRM_Utils_Array::pathGet($coreFields, [$name, 'default'])) {
96 $params[$name] = $field['default_value'];
97 }
98 }
99 }
100
101 /**
102 * Write bao objects as part of a create/update action.
103 *
104 * @param array $items
105 * The records to write to the DB.
106 * @return array
107 * The records after being written to the DB (e.g. including newly assigned "id").
108 * @throws \API_Exception
109 */
110 protected function writeObjects($items) {
111 $baoName = $this->getBaoName();
112
113 // Some BAOs are weird and don't support a straightforward "create" method.
114 $oddballs = [
115 'EntityTag' => 'add',
116 'GroupContact' => 'add',
117 'Website' => 'add',
118 ];
119 $method = $oddballs[$this->getEntityName()] ?? 'create';
120 if (!method_exists($baoName, $method)) {
121 $method = 'add';
122 }
123
124 $result = [];
125
126 foreach ($items as $item) {
2929a8fb 127 $entityId = $item['id'] ?? NULL;
19b53e5b
C
128 FormattingUtil::formatWriteParams($item, $this->getEntityName(), $this->entityFields());
129 $this->formatCustomParams($item, $entityId);
130 $item['check_permissions'] = $this->getCheckPermissions();
131
132 // For some reason the contact bao requires this
133 if ($entityId && $this->getEntityName() == 'Contact') {
134 $item['contact_id'] = $entityId;
135 }
136
137 if ($this->getCheckPermissions()) {
138 $this->checkContactPermissions($baoName, $item);
139 }
140
141 if ($this->getEntityName() == 'Address') {
142 $createResult = $baoName::add($item, $this->fixAddress);
143 }
144 elseif (method_exists($baoName, $method)) {
145 $createResult = $baoName::$method($item);
146 }
147 else {
2ee9afab 148 $createResult = $baoName::writeRecord($item);
19b53e5b
C
149 }
150
151 if (!$createResult) {
152 $errMessage = sprintf('%s write operation failed', $this->getEntityName());
153 throw new \API_Exception($errMessage);
154 }
155
a4c7afc0 156 $result[] = $this->baoToArray($createResult, $item);
19b53e5b 157 }
2929a8fb 158 FormattingUtil::formatOutputValues($result, $this->entityFields(), $this->getEntityName());
19b53e5b
C
159 return $result;
160 }
161
19b53e5b
C
162 /**
163 * @param array $params
164 * @param int $entityId
165 * @return mixed
166 */
167 protected function formatCustomParams(&$params, $entityId) {
168 $customParams = [];
169
170 // $customValueID is the ID of the custom value in the custom table for this
171 // entity (i guess this assumes it's not a multi value entity)
172 foreach ($params as $name => $value) {
173 if (strpos($name, '.') === FALSE) {
174 continue;
175 }
176
177 list($customGroup, $customField) = explode('.', $name);
178
179 $customFieldId = \CRM_Core_BAO_CustomField::getFieldValue(
180 \CRM_Core_DAO_CustomField::class,
181 $customField,
182 'id',
183 'name'
184 );
185 $customFieldType = \CRM_Core_BAO_CustomField::getFieldValue(
186 \CRM_Core_DAO_CustomField::class,
187 $customField,
188 'html_type',
189 'name'
190 );
191 $customFieldExtends = \CRM_Core_BAO_CustomGroup::getFieldValue(
192 \CRM_Core_DAO_CustomGroup::class,
193 $customGroup,
194 'extends',
195 'name'
196 );
197
198 // todo are we sure we don't want to allow setting to NULL? need to test
199 if ($customFieldId && NULL !== $value) {
200
201 if ($customFieldType == 'CheckBox') {
202 // this function should be part of a class
203 formatCheckBoxField($value, 'custom_' . $customFieldId, $this->getEntityName());
204 }
205
206 \CRM_Core_BAO_CustomField::formatCustomField(
207 $customFieldId,
208 $customParams,
209 $value,
210 $customFieldExtends,
211 // todo check when this is needed
212 NULL,
213 $entityId,
214 FALSE,
215 FALSE,
216 TRUE
217 );
218 }
219 }
220
221 if ($customParams) {
222 $params['custom'] = $customParams;
223 }
224 }
225
226 /**
227 * Check edit/delete permissions for contacts and related entities.
228 *
229 * @param $baoName
230 * @param $item
231 * @throws \Civi\API\Exception\UnauthorizedException
232 */
233 protected function checkContactPermissions($baoName, $item) {
234 if ($baoName == 'CRM_Contact_BAO_Contact' && !empty($item['id'])) {
235 $permission = $this->getActionName() == 'delete' ? \CRM_Core_Permission::DELETE : \CRM_Core_Permission::EDIT;
236 if (!\CRM_Contact_BAO_Contact_Permission::allow($item['id'], $permission)) {
237 throw new \Civi\API\Exception\UnauthorizedException('Permission denied to modify contact record');
238 }
239 }
240 else {
241 // Fixme: decouple from v3
242 require_once 'api/v3/utils.php';
2ee9afab 243 _civicrm_api3_check_edit_permissions($baoName, $item);
19b53e5b
C
244 }
245 }
246
247}