122 if (is_array($setDefaultValues))
127 $field = $this->entity->getField($fieldName);
143 if (is_array($subValue))
145 $subValue = $field->getRefEntity()->createObject($subValue);
148 $this->addTo($fieldName, $subValue);
159 if ($setDefaultValues || is_array($setDefaultValues))
161 foreach ($this->entity->getScalarFields() as $fieldName => $field)
163 if ($this->sysHasValue($fieldName))
181 $this->_actualValues = $this->
cloneValues($this->_actualValues);
182 $this->_currentValues = $this->
cloneValues($this->_currentValues);
188 $valuesWithoutReferences = $this->
filterValuesByMask($values, FieldTypeMask::REFERENCE,
true);
189 $references = array_diff_key($values, $valuesWithoutReferences);
191 return array_merge($references, \
Bitrix\
Main\
Type\Collection::clone($valuesWithoutReferences));
196 if ($fieldsMask === FieldTypeMask::ALL)
198 return $invertedFilter ? [] : $values;
201 return array_filter($values,
function($fieldName) use ($fieldsMask, $invertedFilter)
203 $maskOfSingleField = $this->entity->getField($fieldName)->getTypeMask();
204 $matchesMask = (bool)($fieldsMask & $maskOfSingleField);
206 return $invertedFilter ? !$matchesMask: $matchesMask;
207 }, ARRAY_FILTER_USE_KEY);
231 $objectValues = array_merge($this->_actualValues, $this->_currentValues);
235 if ($fieldsMask !== FieldTypeMask::ALL)
237 foreach ($objectValues as $fieldName => $value)
239 $fieldMask = $this->entity->getField($fieldName)->getTypeMask();
240 if (!($fieldsMask & $fieldMask))
242 unset($objectValues[$fieldName]);
250 foreach ($objectValues as $fieldName => $value)
254 $objectValues[$fieldName] = $value->collectValues($valuesType, $fieldsMask, $recursive);
258 $arrayCollection = [];
259 foreach ($value as $relationObject)
261 $arrayCollection[] = $relationObject->collectValues($valuesType, $fieldsMask, $recursive);
263 $objectValues[$fieldName] = $arrayCollection;
271 foreach ($objectValues as
$k => $v)
273 $values[$this->entity->getField(
$k)->getName()] = $v;
290 switch ($this->state)
303 if ($this->_savingInProgress)
308 $this->_savingInProgress =
true;
313 foreach ($this->_currentValues as $fieldName => $currentValue)
315 $field = $this->entity->getField($fieldName);
319 $actualValue = $this->_actualValues[$fieldName];
321 if ($field->encode($currentValue) !== $field->encode($actualValue))
332 unset($this->_currentValues[$fieldName]);
341 $data[
'__object'] = $this;
349 $this->_savingInProgress =
false;
355 foreach (
$result->getPrimary() as $primaryName => $primaryValue)
357 $this->sysSetActual($primaryName, $primaryValue);
360 $this->sysSetValue($primaryName, $primaryValue);
364 $this->sysOnPrimarySet();
369 if (!empty($this->_currentValues))
372 $data[
'__object'] = $this;
380 $this->_savingInProgress =
false;
388 $this->sysSaveRelations(
$result);
396 $this->sysPostSave();
398 $this->_savingInProgress =
false;
410 final public function delete()
415 foreach ($this->entity->getFields() as $field)
419 if ($field->getCascadeDeletePolicy() === CascadePolicy::FOLLOW)
422 $remoteObject = $this->sysGetValue($field->getName());
423 $remoteObject->delete();
428 if ($field->getCascadeDeletePolicy() === CascadePolicy::FOLLOW)
431 $collection = $this->sysFillRelationCollection($field);
433 foreach ($collection as $object)
438 elseif ($field->getCascadeDeletePolicy() === CascadePolicy::SET_NULL)
441 $this->sysRemoveAllFromCollection($field->getName());
444 elseif ($field instanceof ManyToMany)
446 if ($field->getCascadeDeletePolicy() === CascadePolicy::FOLLOW_ORPHANS)
450 elseif ($field->getCascadeDeletePolicy() === CascadePolicy::SET_NULL)
456 $this->sysRemoveAllFromCollection($field->getName());
460 $this->sysSaveRelations(
$result);
464 $dataClass::setCurrentDeletingObject($this);
465 $deleteResult = $dataClass::delete($this->primary);
467 if (!$deleteResult->isSuccess())
469 $result->addErrors($deleteResult->getErrors());
473 foreach ($this->entity->getPrimaryArray()as $primaryName)
475 unset($this->_actualValues[$primaryName]);
492 final public static function wakeUp($row)
495 $objectClass = get_called_class();
500 $entity = $dataClass::getEntity();
501 $entityPrimary =
$entity->getPrimaryArray();
509 if (
count($entityPrimary) == 1)
511 $primary[$entityPrimary[0]] = $row;
516 throw new ArgumentException(sprintf(
517 'Multi-primary for %s was not found', $objectClass
523 foreach ($entityPrimary as $primaryName)
525 if (!isset($row[$primaryName]))
527 throw new ArgumentException(sprintf(
528 'Primary %s for %s was not found', $primaryName, $objectClass
532 $primary[$primaryName] = $row[$primaryName];
533 unset($row[$primaryName]);
539 $object =
new $objectClass(
false);
543 foreach ($primary as $primaryName => $primaryValue)
546 $primaryField =
$entity->getField($primaryName);
547 $object->sysSetActual($primaryName, $primaryField->cast($primaryValue));
551 foreach ($row as $fieldName => $value)
554 $field =
$entity->getField($fieldName);
556 if ($field instanceof IReadable)
558 $object->sysSetActual($fieldName, $field->cast($value));
563 if ($value instanceof
static || $value instanceof Collection)
566 $object->sysSetActual($fieldName, $value);
571 if ($field instanceof Reference)
574 $remoteObjectClass = $field->getRefEntity()->getObjectClass();
575 $remoteObject = $remoteObjectClass::wakeUp($value);
577 $object->sysSetActual($fieldName, $remoteObject);
579 elseif ($field instanceof OneToMany || $field instanceof ManyToMany)
582 $remoteCollectionClass = $field->getRefEntity()->getCollectionClass();
583 $remoteCollection = $remoteCollectionClass::wakeUp($value);
585 $object->sysSetActual($fieldName, $remoteCollection);
603 final public function fill(
$fields = FieldTypeMask::ALL)
606 $primaryFilter = Query::filter();
608 foreach ($this->sysRequirePrimary() as $primaryName => $primaryValue)
610 $primaryFilter->where($primaryName, $primaryValue);
617 $fieldsToSelect = $this->sysGetIdleFields(
$fields);
623 $fieldsToSelect = $this->sysGetIdleFields(
$fields);
628 $fieldsToSelect = $this->sysGetIdleFieldsByMask(
$fields);
631 if (!empty($fieldsToSelect))
633 $fieldsToSelect = array_merge($this->entity->getPrimaryArray(), $fieldsToSelect);
637 $result = $dataClass::query()->setSelect($fieldsToSelect)->where($primaryFilter)->exec();
640 $im =
new IdentityMap;
647 foreach ($fieldsToSelect as $fieldName)
650 if ($this->entity->hasField($fieldName))
652 $field = $this->entity->getField($fieldName);
654 if ($field instanceof OneToMany || $field instanceof ManyToMany)
657 $collection = $this->sysGetValue($fieldName);
659 if (empty($collection))
661 $collection = $field->getRefEntity()->createCollection();
662 $this->_actualValues[$fieldName] = $collection;
665 $collection->sysSetFilled();
674 return $this->sysGetValue(current(
$fields));
688 if (array_key_exists(
'ID', $this->_currentValues))
690 return $this->_currentValues[
'ID'];
692 elseif (array_key_exists(
'ID', $this->_actualValues))
694 return $this->_actualValues[
'ID'];
696 elseif (!$this->entity->hasField(
'ID'))
699 'Unknown method `%s` for object `%s`',
'getId', get_called_class()
715 final public function get($fieldName)
717 return $this->__call(__FUNCTION__, func_get_args());
729 return $this->__call(__FUNCTION__, func_get_args());
739 final public function require($fieldName)
741 return $this->__call(__FUNCTION__, func_get_args());
752 final public function set($fieldName, $value)
754 return $this->__call(__FUNCTION__, func_get_args());
764 final public function reset($fieldName)
766 return $this->__call(__FUNCTION__, func_get_args());
776 final public function unset($fieldName)
778 return $this->__call(__FUNCTION__, func_get_args());
788 final public function has($fieldName)
790 return $this->__call(__FUNCTION__, func_get_args());
800 final public function isFilled($fieldName)
802 return $this->
__call(__FUNCTION__, func_get_args());
812 final public function isChanged($fieldName)
814 return $this->
__call(__FUNCTION__, func_get_args());
825 final public function addTo($fieldName, $value)
827 return $this->
__call(__FUNCTION__, func_get_args());
838 final public function removeFrom($fieldName, $value)
840 return $this->
__call(__FUNCTION__, func_get_args());
850 final public function removeAll($fieldName)
852 return $this->
__call(__FUNCTION__, func_get_args());
855 final public function defineAuthContext(Context $authContext)
857 $this->_authContext = $authContext;
869 public function __get(
$name)
874 return $this->sysGetEntity();
876 return $this->sysGetPrimary();
877 case 'primaryAsString':
878 return $this->sysGetPrimaryAsString();
880 return $this->sysGetState();
882 throw new SystemException(
'Property `dataClass` should be received as static.');
885 if ($this->_customData ===
null)
887 $this->_customData =
new Dictionary;
896 throw new SystemException(sprintf(
897 'Unknown property `%s` for object `%s`',
$name, get_called_class()
909 public function __set(
$name, $value)
914 $this->defineAuthContext($value);
921 throw new SystemException(sprintf(
922 'Property `%s` for object `%s` is read-only',
$name, get_called_class()
926 throw new SystemException(sprintf(
927 'Unknown property `%s` for object `%s`',
$name, get_called_class()
943 $first3 = substr(
$name, 0, 3);
946 if ($first3 ==
'get')
948 $fieldName = self::sysMethodToFieldCase(substr(
$name, 3));
950 if ($fieldName ==
'')
952 $fieldName = StringHelper::strtoupper($arguments[0]);
955 if (array_key_exists($fieldName, $this->_runtimeValues))
957 return $this->sysGetRuntime($fieldName);
961 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
963 if (method_exists($this, $personalMethodName))
965 return $this->$personalMethodName(...array_slice($arguments, 1));
969 $this->entity->getField($fieldName);
973 if ($this->entity->hasField($fieldName))
975 return $this->sysGetValue($fieldName);
980 if ($first3 ==
'set')
982 $fieldName = self::sysMethodToFieldCase(substr(
$name, 3));
985 if ($fieldName ==
'')
987 $fieldName = StringHelper::strtoupper($arguments[0]);
991 if (array_key_exists($fieldName, $this->_runtimeValues))
993 throw new SystemException(sprintf(
994 'Setting value for runtime field `%s` in `%s` is not allowed, it is read-only field',
995 $fieldName, get_called_class()
1000 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1002 if (method_exists($this, $personalMethodName))
1004 return $this->$personalMethodName(...array_slice($arguments, 1));
1008 $this->entity->getField($fieldName);
1012 if ($this->entity->hasField($fieldName))
1014 $field = $this->entity->getField($fieldName);
1016 if ($field instanceof IReadable && !($value instanceof SqlExpression))
1018 $value = $field->cast($value);
1021 return $this->sysSetValue($fieldName, $value);
1025 if ($first3 ==
'has')
1027 $fieldName = self::sysMethodToFieldCase(substr(
$name, 3));
1029 if ($fieldName ==
'')
1031 $fieldName = StringHelper::strtoupper($arguments[0]);
1034 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1036 if (method_exists($this, $personalMethodName))
1038 return $this->$personalMethodName(...array_slice($arguments, 1));
1042 if (array_key_exists($fieldName, $this->_runtimeValues))
1048 $this->entity->getField($fieldName);
1051 if ($this->entity->hasField($fieldName))
1053 return $this->sysHasValue($fieldName);
1057 $first4 = substr(
$name, 0, 4);
1060 if ($first4 ==
'fill')
1062 $fieldName = self::sysMethodToFieldCase(substr(
$name, 4));
1067 if ($this->entity->hasField($fieldName))
1069 return $this->
fill([$fieldName]);
1073 $first5 = substr(
$name, 0, 5);
1076 if ($first5 ==
'addTo')
1078 $fieldName = self::sysMethodToFieldCase(substr(
$name, 5));
1081 if ($fieldName ==
'')
1083 $fieldName = StringHelper::strtoupper($arguments[0]);
1087 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1089 if (method_exists($this, $personalMethodName))
1091 return $this->$personalMethodName(...array_slice($arguments, 1));
1095 $this->entity->getField($fieldName);
1098 if ($this->entity->hasField($fieldName))
1100 $this->sysAddToCollection($fieldName, $value);
1106 if ($first5 ==
'unset')
1108 $fieldName = self::sysMethodToFieldCase(substr(
$name, 5));
1110 if ($fieldName ==
'')
1112 $fieldName = StringHelper::strtoupper($arguments[0]);
1115 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1117 if (method_exists($this, $personalMethodName))
1119 return $this->$personalMethodName(...array_slice($arguments, 1));
1123 $this->entity->getField($fieldName);
1126 if ($this->entity->hasField($fieldName))
1128 return $this->sysUnset($fieldName);
1133 if ($first5 ==
'reset')
1135 $fieldName = self::sysMethodToFieldCase(substr(
$name, 5));
1137 if ($fieldName ==
'')
1139 $fieldName = StringHelper::strtoupper($arguments[0]);
1142 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1144 if (method_exists($this, $personalMethodName))
1146 return $this->$personalMethodName(...array_slice($arguments, 1));
1150 $this->entity->getField($fieldName);
1153 if ($this->entity->hasField($fieldName))
1155 $field = $this->entity->getField($fieldName);
1157 if ($field instanceof OneToMany || $field instanceof ManyToMany)
1159 return $this->sysResetRelation($fieldName);
1163 return $this->sysReset($fieldName);
1168 $first9 = substr(
$name, 0, 9);
1171 if ($first9 ==
'removeAll')
1173 $fieldName = self::sysMethodToFieldCase(substr(
$name, 9));
1175 if ($fieldName ==
'')
1177 $fieldName = StringHelper::strtoupper($arguments[0]);
1180 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1182 if (method_exists($this, $personalMethodName))
1184 return $this->$personalMethodName(...array_slice($arguments, 1));
1188 $this->entity->getField($fieldName);
1191 if ($this->entity->hasField($fieldName))
1193 $this->sysRemoveAllFromCollection($fieldName);
1198 $first10 = substr(
$name, 0, 10);
1201 if ($first10 ==
'removeFrom')
1203 $fieldName = self::sysMethodToFieldCase(substr(
$name, 10));
1206 if ($fieldName ==
'')
1208 $fieldName = StringHelper::strtoupper($arguments[0]);
1212 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1214 if (method_exists($this, $personalMethodName))
1216 return $this->$personalMethodName(...array_slice($arguments, 1));
1220 $this->entity->getField($fieldName);
1223 if ($this->entity->hasField($fieldName))
1225 $this->sysRemoveFromCollection($fieldName, $value);
1230 $first12 = substr(
$name, 0, 12);
1233 if ($first12 ==
'remindActual')
1235 $fieldName = self::sysMethodToFieldCase(substr(
$name, 12));
1237 if ($fieldName ==
'')
1239 $fieldName = StringHelper::strtoupper($arguments[0]);
1242 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1244 if (method_exists($this, $personalMethodName))
1246 return $this->$personalMethodName(...array_slice($arguments, 1));
1250 $this->entity->getField($fieldName);
1254 if ($this->entity->hasField($fieldName))
1256 return $this->_actualValues[$fieldName] ??
null;
1260 $first7 = substr(
$name, 0, 7);
1263 if ($first7 ==
'require')
1265 $fieldName = self::sysMethodToFieldCase(substr(
$name, 7));
1267 if ($fieldName ==
'')
1269 $fieldName = StringHelper::strtoupper($arguments[0]);
1272 $personalMethodName =
$name.static::sysFieldToMethodCase($fieldName);
1274 if (method_exists($this, $personalMethodName))
1276 return $this->$personalMethodName(...array_slice($arguments, 1));
1280 $this->entity->getField($fieldName);
1284 if ($this->entity->hasField($fieldName))
1286 return $this->sysGetValue($fieldName,
true);
1290 $first2 = substr(
$name, 0, 2);
1291 $last6 = substr(
$name, -6);
1294 if ($first2 ==
'is' && $last6 ==
'Filled')
1296 $fieldName = self::sysMethodToFieldCase(substr(
$name, 2, -6));
1298 if ($fieldName ==
'')
1300 $fieldName = StringHelper::strtoupper($arguments[0]);
1303 $personalMethodName = $first2.static::sysFieldToMethodCase($fieldName).$last6;
1305 if (method_exists($this, $personalMethodName))
1307 return $this->$personalMethodName(...array_slice($arguments, 1));
1311 $this->entity->getField($fieldName);
1314 if ($this->entity->hasField($fieldName))
1316 $field = $this->entity->getField($fieldName);
1318 if ($field instanceof OneToMany || $field instanceof ManyToMany)
1320 return array_key_exists($fieldName, $this->_actualValues) && $this->_actualValues[$fieldName]->sysIsFilled();
1324 return $this->sysIsFilled($fieldName);
1329 $last7 = substr(
$name, -7);
1332 if ($first2 ==
'is' && $last7 ==
'Changed')
1334 $fieldName = self::sysMethodToFieldCase(substr(
$name, 2, -7));
1336 if ($fieldName ==
'')
1338 $fieldName = StringHelper::strtoupper($arguments[0]);
1341 $personalMethodName = $first2.static::sysFieldToMethodCase($fieldName).$last7;
1343 if (method_exists($this, $personalMethodName))
1345 return $this->$personalMethodName(...array_slice($arguments, 1));
1349 $this->entity->getField($fieldName);
1352 if ($this->entity->hasField($fieldName))
1354 $field = $this->entity->getField($fieldName);
1356 if ($field instanceof OneToMany || $field instanceof ManyToMany)
1358 return array_key_exists($fieldName, $this->_actualValues) && $this->_actualValues[$fieldName]->sysIsChanged();
1362 return $this->sysIsChanged($fieldName);
1367 throw new SystemException(sprintf(
1368 'Unknown method `%s` for object `%s`',
$name, get_called_class()
1377 public function sysGetEntity()
1379 if ($this->_entity ===
null)
1383 $this->_entity = $dataClass::getEntity();
1396 public function sysGetPrimary()
1398 $primaryValues = [];
1400 foreach ($this->entity->getPrimaryArray() as $primaryName)
1402 $primaryValues[$primaryName] = $this->sysGetValue($primaryName);
1405 return $primaryValues;
1408 public function sysGetPrimaryAsString()
1410 return static::sysSerializePrimary($this->sysGetPrimary(), $this->_entity);
1421 public function sysGetRuntime(
$name)
1423 return $this->_runtimeValues[
$name] ??
null;
1435 public function sysSetRuntime(
$name, $value)
1449 public function sysSetActual($fieldName, $value)
1451 $fieldName = StringHelper::strtoupper($fieldName);
1452 $this->_actualValues[$fieldName] =
$value;
1456 if ($this->entity->getField($fieldName) instanceof ObjectField)
1458 $this->_currentValues[$fieldName] = clone
$value;
1469 public function sysChangeState($state)
1471 if ($this->_state !== $state)
1487 $this->_state = $state;
1499 public function sysGetState()
1514 public function sysGetValue($fieldName, $require =
false)
1516 $fieldName = StringHelper::strtoupper($fieldName);
1518 if (array_key_exists($fieldName, $this->_currentValues))
1520 return $this->_currentValues[$fieldName];
1524 if ($require && !array_key_exists($fieldName, $this->_actualValues))
1526 throw new SystemException(sprintf(
1527 '%s value is required for further operations', $fieldName
1531 return $this->_actualValues[$fieldName] ??
null;
1546 public function sysSetValue($fieldName, $value)
1548 $fieldName = StringHelper::strtoupper($fieldName);
1549 $field = $this->entity->getField($fieldName);
1552 if ($field instanceof ScalarField)
1555 if ($this->_state !==
State::RAW && in_array($field->getName(), $this->entity->getPrimaryArray()))
1557 throw new SystemException(sprintf(
1558 'Setting value for Primary `%s` in `%s` is not allowed, it is read-only field',
1559 $field->getName(), get_called_class()
1565 if ($field instanceof ExpressionField && !($field instanceof UserTypeField))
1567 throw new SystemException(sprintf(
1568 'Setting value for ExpressionField `%s` in `%s` is not allowed, it is read-only field',
1569 $fieldName, get_called_class()
1573 if ($field instanceof Reference)
1578 $remoteObjectClass = $field->getRefEntity()->getObjectClass();
1580 if (!($value instanceof $remoteObjectClass))
1582 throw new ArgumentException(sprintf(
1583 'Expected instance of `%s`, got `%s` instead',
1585 is_object($value) ? get_class($value) : gettype($value)
1595 if (array_key_exists($fieldName, $this->_actualValues) && !($field instanceof UserTypeField))
1597 if ($field instanceof IReadable)
1599 if ($field->cast($value) === $this->_actualValues[$fieldName]
1601 || $field->convertValueToDb($field->modifyValueBeforeSave($value, []))
1602 === $field->convertValueToDb($field->modifyValueBeforeSave($this->_actualValues[$fieldName], []))
1606 unset($this->_currentValues[$fieldName]);
1610 elseif ($field instanceof Reference)
1613 if (
$value->primary === $this->_actualValues[$fieldName]->primary)
1616 unset($this->_currentValues[$fieldName]);
1623 if ($field instanceof ScalarField || $field instanceof UserTypeField)
1625 $this->_currentValues[$fieldName] =
$value;
1627 elseif ($field instanceof Reference)
1630 $this->_currentValues[$fieldName] =
$value;
1633 $elementals = $field->getElementals();
1635 if (!empty($elementals))
1637 $elementalsChanged =
false;
1639 foreach ($elementals as $localFieldName => $remoteFieldName)
1641 if ($this->entity->getField($localFieldName)->isPrimary())
1650 if ($this->state ===
State::RAW && $this->entity->getField($localFieldName)->isAutocomplete())
1656 $remoteField = $field->getRefEntity()->getField($remoteFieldName);
1658 if (!empty($value) && !
$value->sysHasValue($remoteField->getName())
1659 &&
$value->state ===
State::RAW && $remoteField->isPrimary() && $remoteField->isAutocomplete())
1662 $localObject = $this;
1665 $remoteObject->sysAddOnPrimarySetListener(
function () use (
1666 $localObject, $localFieldName, $remoteObject, $remoteFieldName
1668 $localObject->sysSetValue($localFieldName, $remoteObject->get($remoteFieldName));
1673 $elementalValue = empty($value) ? null :
$value->sysGetValue($remoteFieldName);
1674 $this->sysSetValue($localFieldName, $elementalValue);
1677 $elementalsChanged =
true;
1680 if (!$elementalsChanged)
1689 throw new SystemException(sprintf(
1690 'Unknown field type `%s` in system setter of `%s`', get_class($field), get_called_class()
1700 if ($field instanceof ScalarField && $field->isPrimary() && $this->sysHasPrimary())
1702 $this->sysOnPrimarySet();
1715 public function sysHasValue($fieldName)
1717 $fieldName = StringHelper::strtoupper($fieldName);
1719 return $this->sysIsFilled($fieldName) || $this->sysIsChanged($fieldName);
1729 public function sysIsFilled($fieldName)
1731 $fieldName = StringHelper::strtoupper($fieldName);
1733 return array_key_exists($fieldName, $this->_actualValues);
1743 public function sysIsChanged($fieldName)
1745 $fieldName = StringHelper::strtoupper($fieldName);
1746 $field = $this->entity->getField($fieldName);
1748 if ($field instanceof ObjectField)
1750 $currentValue = $this->_currentValues[$fieldName] ??
null;
1751 $actualValue = $this->_actualValues[$fieldName] ??
null;
1753 return $field->encode($currentValue) !== $field->encode($actualValue);
1756 return array_key_exists($fieldName, $this->_currentValues);
1764 public function sysHasPrimary()
1766 foreach ($this->primary as $primaryValue)
1768 if ($primaryValue ===
null)
1780 public function sysOnPrimarySet()
1783 if ($this->sysHasPrimary())
1785 foreach ($this->_onPrimarySetListeners as $listener)
1787 call_user_func($listener, $this);
1797 public function sysAddOnPrimarySetListener($callback)
1800 $this->_onPrimarySetListeners[] = $callback;
1810 public function sysUnset($fieldName)
1812 $fieldName = StringHelper::strtoupper($fieldName);
1814 unset($this->_currentValues[$fieldName]);
1815 unset($this->_actualValues[$fieldName]);
1827 public function sysReset($fieldName)
1829 $fieldName = StringHelper::strtoupper($fieldName);
1831 unset($this->_currentValues[$fieldName]);
1843 public function sysResetRelation($fieldName)
1845 $fieldName = StringHelper::strtoupper($fieldName);
1847 if (isset($this->_actualValues[$fieldName]))
1850 $collection = $this->_actualValues[$fieldName];
1851 $collection->sysResetChanges(
true);
1864 public function sysRequirePrimary()
1866 $primaryValues = [];
1868 foreach ($this->entity->getPrimaryArray() as $primaryName)
1872 $primaryValues[$primaryName] = $this->sysGetValue($primaryName,
true);
1874 catch (SystemException $e)
1876 throw new SystemException(sprintf(
1877 'Primary `%s` value is required for further operations', $primaryName
1882 return $primaryValues;
1894 public function sysGetIdleFields(
$fields = [])
1901 $fields = array_keys($this->entity->getFields());
1904 foreach (
$fields as $fieldName)
1906 $fieldName = StringHelper::strtoupper($fieldName);
1908 if (!array_key_exists($fieldName, $this->_actualValues))
1911 $list[] = $fieldName;
1913 elseif ($this->_actualValues[$fieldName] instanceof Collection && !$this->_actualValues[$fieldName]->sysIsFilled())
1916 $list[] = $fieldName;
1932 public function sysGetIdleFieldsByMask($mask = FieldTypeMask::ALL)
1936 foreach ($this->entity->getFields() as $field)
1938 $fieldMask = $field->getTypeMask();
1941 !array_key_exists(StringHelper::strtoupper($field->getName()), $this->_actualValues)
1942 && ($mask & $fieldMask)
1945 $list[] = $field->getName();
1960 public function sysSaveRelations(Result
$result)
1962 $saveCascade =
true;
1964 foreach ($this->_actualValues as $fieldName => $value)
1966 $field = $this->entity->getField($fieldName);
1968 if ($field instanceof Reference && !array_key_exists($fieldName, $this->_currentValues))
1971 if ($saveCascade && !empty($value))
1977 elseif ($field instanceof OneToMany)
1982 $objectsToSave = [];
1985 $objectsToDelete = [];
1987 if ($collection->sysIsChanged())
1990 foreach ($collection->sysGetChanges() as $change)
1992 [$remoteObject, $changeType] = $change;
1996 $objectsToSave[] = $remoteObject;
2000 if ($field->getCascadeDeletePolicy() == CascadePolicy::FOLLOW)
2002 $objectsToDelete[] = $remoteObject;
2007 $objectsToSave[] = $remoteObject;
2016 foreach ($collection->getAll() as $remoteObject)
2018 if (!in_array($remoteObject, $objectsToDelete) && !in_array($remoteObject, $objectsToSave))
2020 $objectsToSave[] = $remoteObject;
2026 foreach ($objectsToSave as $remoteObject)
2028 $remoteResult = $remoteObject->save();
2030 if (!$remoteResult->isSuccess())
2032 $result->addErrors($remoteResult->getErrors());
2037 foreach ($objectsToDelete as $remoteObject)
2039 $remoteResult = $remoteObject->delete();
2041 if (!$remoteResult->isSuccess())
2043 $result->addErrors($remoteResult->getErrors());
2048 if ($collection->sysIsChanged())
2050 $collection->sysResetChanges();
2053 elseif ($field instanceof ManyToMany)
2057 if (
$value->sysIsChanged())
2059 foreach ($collection->sysGetChanges() as $change)
2061 [$remoteObject, $changeType] = $change;
2064 $mediatorObjectClass = $field->getMediatorEntity()->getObjectClass();
2065 $localReferenceName = $field->getLocalReferenceName();
2066 $remoteReferenceName = $field->getRemoteReferenceName();
2069 $mediatorObject =
new $mediatorObjectClass;
2070 $mediatorObject->sysSetValue($localReferenceName, $this);
2071 $mediatorObject->sysSetValue($remoteReferenceName, $remoteObject);
2076 $mediatorObject->save();
2081 $mediatorDataClass = $field->getMediatorEntity()->getDataClass();
2082 $mediatorDataClass::delete($mediatorObject->primary);
2087 $collection->sysResetChanges();
2093 foreach ($collection->getAll() as $remoteObject)
2095 $remoteResult = $remoteObject->save();
2097 if (!$remoteResult->isSuccess())
2099 $result->addErrors($remoteResult->getErrors());
2106 if ($value instanceof Collection)
2108 $value->sysReviseDeletedObjects();
2114 $this->sysSaveCurrentReferences();
2118 public function sysSaveCurrentReferences()
2120 foreach ($this->_currentValues as $fieldName => $value)
2122 if ($this->entity->getField($fieldName) instanceof Reference && !empty($value))
2129 public function sysPostSave()
2132 foreach ($this->_currentValues as
$k => $v)
2134 $field = $this->entity->getField(
$k);
2137 if ($v instanceof EntityObject)
2148 $this->sysSetActual(
$k, $v);
2151 elseif ($field instanceof ScalarField || $field instanceof UserTypeField)
2153 $v = $field->cast($v);
2155 if ($v instanceof SqlExpression)
2160 $this->sysSetActual(
$k, $v);
2164 unset($this->_currentValues[
$k]);
2171 foreach ($this->_actualValues as $fieldName => $actualValue)
2173 if ($this->entity->getField($fieldName) instanceof ObjectField)
2175 $this->_currentValues[$fieldName] = clone $actualValue;
2189 public function sysAddToCollection($fieldName, $remoteObject)
2191 $fieldName = StringHelper::strtoupper($fieldName);
2194 $field = $this->entity->getField($fieldName);
2195 $remoteObjectClass = $field->getRefEntity()->getObjectClass();
2198 if (!($remoteObject instanceof $remoteObjectClass))
2200 throw new ArgumentException(sprintf(
2201 'Expected instance of `%s`, got `%s` instead', $remoteObjectClass, get_class($remoteObject)
2206 $collection = $this->sysGetValue($fieldName);
2208 if (empty($collection))
2210 $collection = $field->getRefEntity()->createCollection();
2211 $this->_actualValues[$fieldName] = $collection;
2214 if ($field instanceof OneToMany)
2217 $remoteFieldName = $field->getRefField()->getName();
2218 $remoteObject->sysSetValue($remoteFieldName, $this);
2223 $localObject = $this;
2225 $this->sysAddOnPrimarySetListener(
function () use ($localObject, $remoteObject, $remoteFieldName) {
2226 $remoteObject->sysSetValue($remoteFieldName, $localObject);
2232 $collection->add($remoteObject);
2250 public function sysRemoveFromCollection($fieldName, $remoteObject)
2252 $fieldName = StringHelper::strtoupper($fieldName);
2255 $field = $this->entity->getField($fieldName);
2256 $remoteObjectClass = $field->getRefEntity()->getObjectClass();
2259 if (!($remoteObject instanceof $remoteObjectClass))
2261 throw new ArgumentException(sprintf(
2262 'Expected instance of `%s`, got `%s` instead', $remoteObjectClass, get_class($remoteObject)
2267 $collection = $this->sysGetValue($fieldName);
2269 if (empty($collection))
2271 $collection = $field->getRefEntity()->createCollection();
2272 $this->_actualValues[$fieldName] = $collection;
2276 $collection->remove($remoteObject);
2278 if ($field instanceof OneToMany)
2281 if ($field->getCascadeDeletePolicy() == CascadePolicy::FOLLOW)
2288 $remoteFieldName = $field->getRefField()->getName();
2289 $remoteObject->sysSetValue($remoteFieldName,
null);
2309 public function sysRemoveAllFromCollection($fieldName)
2311 $fieldName = StringHelper::strtoupper($fieldName);
2312 $collection = $this->sysFillRelationCollection($fieldName);
2315 foreach ($collection as $remoteObject)
2317 $this->sysRemoveFromCollection($fieldName, $remoteObject);
2328 public function sysFillRelationCollection($field)
2330 if ($field instanceof Relation)
2332 $fieldName = $field->getName();
2336 $fieldName = $field;
2337 $field = $this->entity->getField($fieldName);
2341 $collection = $this->sysGetValue($fieldName);
2343 if (empty($collection))
2345 $collection = $field->getRefEntity()->createCollection();
2346 $this->_actualValues[$fieldName] = $collection;
2349 if (!$collection->sysIsFilled())
2352 $remotePrimaryDefinitions = [];
2354 foreach ($field->getRefEntity()->getPrimaryArray() as $primaryName)
2356 $remotePrimaryDefinitions[] = $fieldName.
'.'.$primaryName;
2359 $this->
fill($remotePrimaryDefinitions);
2362 $collection->sysSetFilled();
2375 public static function sysMethodToFieldCase($methodName)
2377 if (!isset(static::$_camelToSnakeCache[$methodName]))
2379 static::$_camelToSnakeCache[$methodName] = StringHelper::strtoupper(
2380 StringHelper::camel2snake($methodName)
2384 return static::$_camelToSnakeCache[$methodName];
2394 public static function sysFieldToMethodCase($fieldName)
2396 if (!isset(static::$_snakeToCamelCache[$fieldName]))
2398 static::$_snakeToCamelCache[$fieldName] = StringHelper::snake2camel($fieldName);
2401 return static::$_snakeToCamelCache[$fieldName];
2411 public static function sysSerializePrimary($primary,
$entity)
2415 return (
string) current($primary);
2418 return (
string) Json::encode(array_values($primary));
2432 return $this->sysHasValue($offset) && $this->sysGetValue($offset) !==
null;
2444 #[\ReturnTypeWillChange]
2450 return $this->
get($offset);
2452 elseif (array_key_exists($offset, $this->_runtimeValues))
2455 return $this->sysGetRuntime($offset);
2458 return $this->
offsetExists($offset) ? $this->
get($offset) : null;
2470 public function offsetSet($offset, $value): void
2472 if (is_null($offset))
2474 throw new ArgumentException(
'Field name should be set');
2478 $this->
set($offset,
$value);
2489 $this->unset($offset);