32 abstract class Collection implements \ArrayAccess, \Iterator, \Countable
82 if (__CLASS__ !== get_called_class())
86 $this->_entity = $dataClass::getEntity();
95 $this->_entity = $entity;
98 $this->_objectClass = $this->_entity->getObjectClass();
99 $this->_isSinglePrimary =
count($this->_entity->getPrimaryArray()) == 1;
111 if (!($object instanceof $this->_objectClass))
114 'Invalid object class %s for %s collection, expected "%s".',
115 get_class($object), get_class($this), $this->_objectClass
121 if (!$object->sysHasPrimary())
124 $object->sysAddOnPrimarySetListener([$this,
'sysOnObjectPrimarySet']);
127 if (empty($this->_objects[$srPrimary])
128 && (!isset($this->_objectsChanges[$srPrimary]) || $this->_objectsChanges[$srPrimary] != static::OBJECT_REMOVED))
130 $this->_objects[$srPrimary] = $object;
131 $this->_objectsChanges[$srPrimary] = static::OBJECT_ADDED;
133 elseif (isset($this->_objectsChanges[$srPrimary]) && $this->_objectsChanges[$srPrimary] == static::OBJECT_REMOVED)
136 $this->_objects[$srPrimary] = $object;
138 unset($this->_objectsChanges[$srPrimary]);
139 unset($this->_objectsRemoved[$srPrimary]);
153 if (!($object instanceof $this->_objectClass))
156 'Invalid object class %s for %s collection, expected "%s".',
157 get_class($object), get_class($this), $this->_objectClass
193 return array_values($this->_objects);
206 if (!($object instanceof $this->_objectClass))
209 'Invalid object class %s for %s collection, expected "%s".',
210 get_class($object), get_class($this), $this->_objectClass
239 $object = $this->_objects[$srPrimary];
240 unset($this->_objects[$srPrimary]);
242 if (!isset($this->_objectsChanges[$srPrimary]) || $this->_objectsChanges[$srPrimary] != static::OBJECT_ADDED)
245 $this->_objectsChanges[$srPrimary] = static::OBJECT_REMOVED;
246 $this->_objectsRemoved[$srPrimary] = $object;
248 elseif (isset($this->_objectsChanges[$srPrimary]) && $this->_objectsChanges[$srPrimary] == static::OBJECT_ADDED)
251 unset($this->_objectsChanges[$srPrimary]);
252 unset($this->_objectsRemoved[$srPrimary]);
266 $entityPrimary = $this->_entity->getPrimaryArray();
269 $fieldsToSelect = [];
272 if (is_scalar($fields) && !is_numeric($fields))
278 foreach ($this->_objects as $object)
280 $idleFields = is_array($fields)
281 ? $object->sysGetIdleFields($fields)
282 : $object->sysGetIdleFieldsByMask($fields);
284 if (!empty($idleFields))
286 $fieldsToSelect = array_unique(array_merge($fieldsToSelect, $idleFields));
289 $objectPrimary = $object->sysRequirePrimary();
291 $primaryValues[] =
count($objectPrimary) == 1
298 if (!empty($fieldsToSelect))
300 $fieldsToSelect = array_unique(array_merge($entityPrimary, $fieldsToSelect));
305 if (
count($entityPrimary) == 1)
308 $primaryFilter->whereIn($entityPrimary[0], $primaryValues);
313 $primaryFilter->logic(
'or');
315 foreach ($primaryValues as $objectPrimary)
320 foreach ($objectPrimary as $primaryName => $primaryValue)
322 $oneObjectFilter->where($primaryName, $primaryValue);
325 $primaryFilter->where($oneObjectFilter);
331 $result = $dataClass::query()->setSelect($fieldsToSelect)->where($primaryFilter)->exec();
336 foreach ($this->_objects as $object)
341 $result->setIdentityMap($im);
342 $result->fetchCollection();
346 if (is_array($fields) &&
count($fields) == 1 && $this->entity->hasField(
current($fields)))
349 $field = $this->entity->getField($fieldName);
357 final public function save($ignoreEvents =
false)
367 foreach ($this->_objects as $object)
371 $addObjects[] = [
'__object' => $object];
375 $updateObjects[] = $object;
382 if (!empty($addObjects))
384 $result = $dataClass::addMulti($addObjects, $ignoreEvents);
388 if (!empty($updateObjects))
397 foreach ($updateObjects as $object)
402 if ($dataSample !== $objectData)
408 $primaries[] = $object->primary;
414 $result = $dataClass::updateMulti($primaries, $dataSample, $ignoreEvents);
417 foreach ($updateObjects as $object)
419 $object->sysSaveRelations($result);
420 $object->sysPostSave();
426 foreach ($updateObjects as $object)
428 $objectResult = $object->save();
430 if (!$objectResult->isSuccess())
432 $result->addErrors($objectResult->getErrors());
450 final public static function wakeUp($rows)
454 $objectClass = $dataClass::getObjectClass();
457 $collection =
new static;
459 foreach ($rows as $row)
461 $collection->sysAddActual($objectClass::wakeUp($row));
482 throw new SystemException(
'Property `dataClass` should be received as static.');
486 'Unknown property `%s` for collection `%s`', $name, get_called_class()
498 public function __set($name, $value)
505 'Property `%s` for collection `%s` is read-only', $name, get_called_class()
510 'Unknown property `%s` for collection `%s`', $name, get_called_class()
524 public function __call($name, $arguments)
526 $first3 = substr($name, 0, 3);
527 $last4 = substr($name, -4);
530 if ($first3 ==
'get' && $last4 ==
'List')
532 $fieldName = EntityObject::sysMethodToFieldCase(substr($name, 3, -4));
534 if (!strlen($fieldName))
539 $personalMethodName = $first3.EntityObject::sysFieldToMethodCase($fieldName).$last4;
541 if (method_exists($this, $personalMethodName))
543 return $this->$personalMethodName(...array_slice($arguments, 1));
547 $this->entity->getField($fieldName);
551 if ($this->_entity->hasField($fieldName))
557 $last10 = substr($name, -10);
559 if ($first3 ==
'get' && $last10 ==
'Collection')
561 $fieldName = EntityObject::sysMethodToFieldCase(substr($name, 3, -10));
563 if (!strlen($fieldName))
568 $personalMethodName = $first3.EntityObject::sysFieldToMethodCase($fieldName).$last10;
570 if (method_exists($this, $personalMethodName))
572 return $this->$personalMethodName(...array_slice($arguments, 1));
576 $this->entity->getField($fieldName);
580 if ($this->_entity->hasField($fieldName) && $this->_entity->getField($fieldName) instanceof
Relation)
586 $first4 = substr($name, 0, 4);
589 if ($first4 ==
'fill')
591 $fieldName = EntityObject::sysMethodToFieldCase(substr($name, 4));
594 if ($this->_entity->hasField($fieldName))
596 return $this->
fill([$fieldName]);
601 'Unknown method `%s` for object `%s`', $name, get_called_class()
625 $srHash = spl_object_hash($object);
628 if (isset($this->_objects[$srHash]))
631 unset($this->_objects[$srHash]);
632 $this->_objects[$srPrimary] = $object;
635 if (isset($this->_objectsChanges[$srHash]))
637 $this->_objectsChanges[$srPrimary] = $this->_objectsChanges[$srHash];
638 unset($this->_objectsChanges[$srHash]);
642 if (isset($this->_objectsRemoved[$srHash]))
644 $this->_objectsRemoved[$srPrimary] = $this->_objectsRemoved[$srHash];
645 unset($this->_objectsRemoved[$srHash]);
667 return !empty($this->_objectsChanges);
680 foreach ($this->_objectsChanges as $srPrimary => $changeCode)
682 if (isset($this->_objects[$srPrimary]))
684 $changedObject = $this->_objects[$srPrimary];
686 elseif (isset($this->_objectsRemoved[$srPrimary]))
688 $changedObject = $this->_objectsRemoved[$srPrimary];
692 $changedObject =
null;
695 if (empty($changedObject))
698 'Object with primary `%s` was not found in `%s` collection', $srPrimary, get_class($this)
702 $changes[] = [$changedObject, $changeCode];
717 foreach ($this->_objectsChanges as $srPrimary => $changeCode)
719 if ($changeCode === static::OBJECT_ADDED)
721 unset($this->_objects[$srPrimary]);
723 elseif ($changeCode === static::OBJECT_REMOVED)
725 $this->_objects[$srPrimary] = $this->_objectsRemoved[$srPrimary];
730 $this->_objectsChanges = [];
731 $this->_objectsRemoved = [];
745 foreach ($this->_objects as $objectPrimary => $object)
747 $values[] = $object->sysGetValue($fieldName);
763 $field = $this->_entity->getField($fieldName);
765 $values = $field->getRefEntity()->createCollection();
768 foreach ($this->_objects as $objectPrimary => $object)
770 $value = $object->sysGetValue($fieldName);
778 foreach ($value->getAll() as $remoteObject)
780 $values[] = $remoteObject;
794 foreach ($this->_objects as $k => $object)
798 unset($this->_objects[$k]);
810 $this->_isFilled = $value;
824 $primaryNames = $this->_entity->getPrimaryArray();
826 if (!is_array($primary))
828 if (
count($primaryNames) > 1)
831 'Only one value of primary found, when entity %s has %s primary keys',
832 $this->_entity->getDataClass(),
count($primaryNames)
836 $primary = [$primaryNames[0] => $primary];
840 $normalizedPrimary = [];
842 foreach ($primaryNames as $primaryName)
845 $field = $this->_entity->getField($primaryName);
846 $normalizedPrimary[$primaryName] = $field->cast($primary[$primaryName]);
849 return $normalizedPrimary;
863 if ($object->sysHasPrimary())
869 return spl_object_hash($object);
883 if ($this->_isSinglePrimary)
949 reset($this->_iterableObjects);
959 if ($this->_iterableObjects ===
null)
964 return current($this->_iterableObjects);
974 return key($this->_iterableObjects);
982 next($this->_iterableObjects);
992 return key($this->_iterableObjects) !==
null;
1002 return count($this->_objects);