4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
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 |
10 +--------------------------------------------------------------------+
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
21 namespace Civi\Api4\Generic\Traits
;
23 use Civi\Api4\Utils\FormattingUtil
;
26 * @method string getLanguage()
27 * @method setLanguage(string $language)
29 trait DAOActionTrait
{
32 * Specify the language to use if this is a multi-lingual environment.
34 * E.g. "en_US" or "fr_CA"
41 * @return \CRM_Core_DAO|string
43 protected function getBaoName() {
44 require_once 'api/v3/utils.php';
45 return \
_civicrm_api3_get_BAO($this->getEntityName());
49 * Convert saved object to array
51 * Used by create, update & save actions
53 * @param \CRM_Core_DAO $bao
57 public function baoToArray($bao, $input) {
58 $allFields = array_column($bao->fields(), 'name');
59 if (!empty($this->reload
)) {
60 $inputFields = $allFields;
64 $inputFields = array_keys($input);
65 // Convert 'null' input to true null
66 foreach ($input as $key => $val) {
67 if ($val === 'null') {
73 foreach ($allFields as $field) {
74 if (isset($bao->$field) ||
in_array($field, $inputFields)) {
75 $values[$field] = $bao->$field ??
NULL;
82 * Fill field defaults which were declared by the api.
84 * Note: default values from core are ignored because the BAO or database layer will supply them.
86 * @param array $params
88 protected function fillDefaults(&$params) {
89 $fields = $this->entityFields();
90 $bao = $this->getBaoName();
91 $coreFields = array_column($bao::fields(), NULL, 'name');
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'];
102 * Write bao objects as part of a create/update action.
104 * @param array $items
105 * The records to write to the DB.
107 * The records after being written to the DB (e.g. including newly assigned "id").
108 * @throws \API_Exception
110 protected function writeObjects($items) {
111 $baoName = $this->getBaoName();
113 // Some BAOs are weird and don't support a straightforward "create" method.
115 'EntityTag' => 'add',
116 'GroupContact' => 'add',
119 $method = $oddballs[$this->getEntityName()] ??
'create';
120 if (!method_exists($baoName, $method)) {
126 foreach ($items as $item) {
127 $entityId = $item['id'] ??
NULL;
128 FormattingUtil
::formatWriteParams($item, $this->getEntityName(), $this->entityFields());
129 $this->formatCustomParams($item, $entityId);
130 $item['check_permissions'] = $this->getCheckPermissions();
132 // For some reason the contact bao requires this
133 if ($entityId && $this->getEntityName() == 'Contact') {
134 $item['contact_id'] = $entityId;
137 if ($this->getCheckPermissions()) {
138 $this->checkContactPermissions($baoName, $item);
141 if ($this->getEntityName() == 'Address') {
142 $createResult = $baoName::add($item, $this->fixAddress
);
144 elseif (method_exists($baoName, $method)) {
145 $createResult = $baoName::$method($item);
148 $createResult = $baoName::writeRecord($item);
151 if (!$createResult) {
152 $errMessage = sprintf('%s write operation failed', $this->getEntityName());
153 throw new \
API_Exception($errMessage);
156 $result[] = $this->baoToArray($createResult, $item);
158 FormattingUtil
::formatOutputValues($result, $this->entityFields(), $this->getEntityName());
163 * @param array $params
164 * @param int $entityId
167 protected function formatCustomParams(&$params, $entityId) {
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) {
177 list($customGroup, $customField) = explode('.', $name);
178 list($customField, $option) = array_pad(explode(':', $customField), 2, NULL);
180 $customFieldId = \CRM_Core_BAO_CustomField
::getFieldValue(
181 \CRM_Core_DAO_CustomField
::class,
186 $customFieldType = \CRM_Core_BAO_CustomField
::getFieldValue(
187 \CRM_Core_DAO_CustomField
::class,
192 $customFieldExtends = \CRM_Core_BAO_CustomGroup
::getFieldValue(
193 \CRM_Core_DAO_CustomGroup
::class,
199 // todo are we sure we don't want to allow setting to NULL? need to test
200 if ($customFieldId && NULL !== $value) {
203 $options = FormattingUtil
::getPseudoconstantList($this->getEntityName(), 'custom_' . $customFieldId, $option, $params, $this->getActionName());
204 $value = FormattingUtil
::replacePseudoconstant($options, $value, TRUE);
207 if ($customFieldType == 'CheckBox') {
208 // this function should be part of a class
209 formatCheckBoxField($value, 'custom_' . $customFieldId, $this->getEntityName());
212 \CRM_Core_BAO_CustomField
::formatCustomField(
217 // todo check when this is needed
228 $params['custom'] = $customParams;
233 * Check edit/delete permissions for contacts and related entities.
237 * @throws \Civi\API\Exception\UnauthorizedException
239 protected function checkContactPermissions($baoName, $item) {
240 if ($baoName == 'CRM_Contact_BAO_Contact' && !empty($item['id'])) {
241 $permission = $this->getActionName() == 'delete' ? \CRM_Core_Permission
::DELETE
: \CRM_Core_Permission
::EDIT
;
242 if (!\CRM_Contact_BAO_Contact_Permission
::allow($item['id'], $permission)) {
243 throw new \Civi\API\Exception\
UnauthorizedException('Permission denied to modify contact record');
247 // Fixme: decouple from v3
248 require_once 'api/v3/utils.php';
249 _civicrm_api3_check_edit_permissions($baoName, $item);