/** * Computes the changes of an association. * * @param array $assoc The association mapping. * @param mixed $value The value of the association. * * @throws ORMInvalidArgumentException * @throws ORMException * * @return void */ private function computeAssociationChanges($assoc, $value) { if ($value instanceof Proxy && !$value->__isInitialized__) { return; } if ($value instanceof PersistentCollection && $value->isDirty()) { $coid = spl_object_hash($value); if ($assoc['isOwningSide']) { $this->collectionUpdates[$coid] = $value; } $this->visitedCollections[$coid] = $value; } // Look through the entities, and in any of their associations, // for transient (new) entities, recursively. ("Persistence by reachability") // Unwrap. Uninitialized collections will simply be empty. $unwrappedValue = $assoc['type'] & ClassMetadata::TO_ONE ? array($value) : $value->unwrap(); $targetClass = $this->em->getClassMetadata($assoc['targetEntity']); foreach ($unwrappedValue as $key => $entry) { $state = $this->getEntityState($entry, self::STATE_NEW); if (!$entry instanceof $assoc['targetEntity']) { throw ORMException::unexpectedAssociationValue($assoc['sourceEntity'], $assoc['fieldName'], get_class($entry), $assoc['targetEntity']); } switch ($state) { case self::STATE_NEW: if (!$assoc['isCascadePersist']) { throw ORMInvalidArgumentException::newEntityFoundThroughRelationship($assoc, $entry); } $this->persistNew($targetClass, $entry); $this->computeChangeSet($targetClass, $entry); break; case self::STATE_REMOVED: // Consume the $value as array (it's either an array or an ArrayAccess) // and remove the element from Collection. if ($assoc['type'] & ClassMetadata::TO_MANY) { unset($value[$key]); } break; case self::STATE_DETACHED: // Can actually not happen right now as we assume STATE_NEW, // so the exception will be raised from the DBAL layer (constraint violation). throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); break; default: // MANAGED associated entities are already taken into account // during changeset calculation anyway, since they are in the identity map. } } }