public function testHandleLoggable() { $loggableCollectionClass = new LoggableCollectionClass(); $loggableCollectionClass->setName('testCollectionName'); $collection = new PersistentCollection($this->em, get_class($loggableCollectionClass), array($loggableCollectionClass)); $collection->setDirty(true); $this->loggableClass->setCollection($collection); $this->em->persist($this->loggableClass); //log with out user $this->loggableManager->handleLoggable($this->em); //log with user $this->loggableManager->setUsername('testUser'); $this->loggableManager->handleLoggable($this->em); //log delete $this->em->remove($this->loggableClass); $this->loggableManager->handleLoggable($this->em); }
/** * Computes the changes that happened to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @ignore * * @internal Don't call from the outside. * * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. * * @return void */ public function computeChangeSet(ClassMetadata $class, $entity) { $oid = spl_object_hash($entity); if (isset($this->readOnlyObjects[$oid])) { return; } if (!$class->isInheritanceTypeNone()) { $class = $this->em->getClassMetadata(get_class($entity)); } $invoke = $this->listenersInvoker->getSubscribedSystems($class, Events::preFlush) & ~ListenersInvoker::INVOKE_MANAGER; if ($invoke !== ListenersInvoker::INVOKE_NONE) { $this->listenersInvoker->invoke($class, Events::preFlush, $entity, new PreFlushEventArgs($this->em), $invoke); } $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity); if ($class->isCollectionValuedAssociation($name) && $value !== null) { if ($value instanceof PersistentCollection) { if ($value->getOwner() === $entity) { continue; } $value = new ArrayCollection($value->getValues()); } // If $value is not a Collection then use an ArrayCollection. if (!$value instanceof Collection) { $value = new ArrayCollection($value); } $assoc = $class->associationMappings[$name]; // Inject PersistentCollection $value = new PersistentCollection($this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value); $value->setOwner($entity, $assoc); $value->setDirty(!$value->isEmpty()); $class->reflFields[$name]->setValue($entity, $value); $actualData[$name] = $value; continue; } if ((!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) && $name !== $class->versionField) { $actualData[$name] = $value; } } if (!isset($this->originalEntityData[$oid])) { // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). // These result in an INSERT. $this->originalEntityData[$oid] = $actualData; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { if (!isset($class->associationMappings[$propName])) { $changeSet[$propName] = array(null, $actualValue); continue; } $assoc = $class->associationMappings[$propName]; if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { $changeSet[$propName] = array(null, $actualValue); } } $this->entityChangeSets[$oid] = $changeSet; } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->originalEntityData[$oid]; $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $changeSet = $isChangeTrackingNotify && isset($this->entityChangeSets[$oid]) ? $this->entityChangeSets[$oid] : array(); foreach ($actualData as $propName => $actualValue) { // skip field, its a partially omitted one! if (!(isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { continue; } $orgValue = $originalData[$propName]; // skip if value haven't changed if ($orgValue === $actualValue) { continue; } // if regular field if (!isset($class->associationMappings[$propName])) { if ($isChangeTrackingNotify) { continue; } $changeSet[$propName] = array($orgValue, $actualValue); continue; } $assoc = $class->associationMappings[$propName]; // Persistent collection was exchanged with the "originally" // created one. This can only mean it was cloned and replaced // on another entity. if ($actualValue instanceof PersistentCollection) { $owner = $actualValue->getOwner(); if ($owner === null) { // cloned $actualValue->setOwner($entity, $assoc); } else { if ($owner !== $entity) { // no clone, we have to fix if (!$actualValue->isInitialized()) { $actualValue->initialize(); // we have to do this otherwise the cols share state } $newValue = clone $actualValue; $newValue->setOwner($entity, $assoc); $class->reflFields[$propName]->setValue($entity, $newValue); } } } if ($orgValue instanceof PersistentCollection) { // A PersistentCollection was de-referenced, so delete it. $coid = spl_object_hash($orgValue); if (isset($this->collectionDeletions[$coid])) { continue; } $this->collectionDeletions[$coid] = $orgValue; $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. continue; } if ($assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide']) { $changeSet[$propName] = array($orgValue, $actualValue); } if ($orgValue !== null && $assoc['orphanRemoval']) { $this->scheduleOrphanRemoval($orgValue); } } } if ($changeSet) { $this->entityChangeSets[$oid] = $changeSet; $this->originalEntityData[$oid] = $actualData; $this->entityUpdates[$oid] = $entity; } } // Look for changes in associations of the entity foreach ($class->associationMappings as $field => $assoc) { if (($val = $class->reflFields[$field]->getValue($entity)) === null) { continue; } $this->computeAssociationChanges($assoc, $val); if (!isset($this->entityChangeSets[$oid]) && $assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY && $val instanceof PersistentCollection && $val->isDirty()) { $this->entityChangeSets[$oid] = array(); $this->originalEntityData[$oid] = $actualData; $this->entityUpdates[$oid] = $entity; } } }
/** * Computes the changes that happened to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ public function computeChangeSet(ClassMetadata $class, $entity) { if (!$class->isInheritanceTypeNone()) { $class = $this->em->getClassMetadata(get_class($entity)); } $oid = spl_object_hash($entity); $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity); if ($class->isCollectionValuedAssociation($name) && $value !== null && !$value instanceof PersistentCollection) { // If $value is not a Collection then use an ArrayCollection. if (!$value instanceof Collection) { $value = new ArrayCollection($value); } $assoc = $class->associationMappings[$name]; // Inject PersistentCollection $coll = new PersistentCollection($this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value); $coll->setOwner($entity, $assoc); $coll->setDirty(!$coll->isEmpty()); $class->reflFields[$name]->setValue($entity, $coll); $actualData[$name] = $coll; } else { if (!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) { $actualData[$name] = $value; } } } if (!isset($this->originalEntityData[$oid])) { // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). // These result in an INSERT. $this->originalEntityData[$oid] = $actualData; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { $changeSet[$propName] = array(null, $actualValue); } } else { $changeSet[$propName] = array(null, $actualValue); } } $this->entityChangeSets[$oid] = $changeSet; } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->originalEntityData[$oid]; $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $changeSet = $isChangeTrackingNotify ? $this->entityChangeSets[$oid] : array(); foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; if ($assoc['type'] & ClassMetadata::TO_ONE && $orgValue !== $actualValue) { if ($assoc['isOwningSide']) { $changeSet[$propName] = array($orgValue, $actualValue); } if ($orgValue !== null && $assoc['orphanRemoval']) { $this->scheduleOrphanRemoval($orgValue); } } else { if ($orgValue instanceof PersistentCollection && $orgValue !== $actualValue) { // A PersistentCollection was de-referenced, so delete it. if (!in_array($orgValue, $this->collectionDeletions, true)) { $this->collectionDeletions[] = $orgValue; $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. } } } } else { if ($isChangeTrackingNotify) { continue; } else { if (is_object($orgValue) && $orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) { $changeSet[$propName] = array($orgValue, $actualValue); } } } } } if ($changeSet) { $this->entityChangeSets[$oid] = $changeSet; $this->originalEntityData[$oid] = $actualData; $this->entityUpdates[$oid] = $entity; } } // Look for changes in associations of the entity foreach ($class->associationMappings as $field => $assoc) { $val = $class->reflFields[$field]->getValue($entity); if ($val !== null) { $this->computeAssociationChanges($assoc, $val); } } }
/** * Computes the changes done to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ private function _computeEntityChanges($class, $entity) { $oid = spl_object_hash($entity); if (!$class->isInheritanceTypeNone()) { $class = $this->_em->getClassMetadata(get_class($entity)); } $actualData = array(); foreach ($class->reflFields as $name => $refProp) { if (!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) { $actualData[$name] = $refProp->getValue($entity); } if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null && !$actualData[$name] instanceof PersistentCollection) { //TODO: If $actualData[$name] is Collection then unwrap the array $assoc = $class->associationMappings[$name]; //echo PHP_EOL . "INJECTING PCOLL into $name" . PHP_EOL; // Inject PersistentCollection $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $actualData[$name] ? $actualData[$name] : array()); $coll->setOwner($entity, $assoc); if (!$coll->isEmpty()) { $coll->setDirty(true); } $class->reflFields[$name]->setValue($entity, $coll); $actualData[$name] = $coll; } } if (!isset($this->_originalEntityData[$oid])) { // Entity is either NEW or MANAGED but not yet fully persisted // (only has an id). These result in an INSERT. $this->_originalEntityData[$oid] = $actualData; $this->_entityChangeSets[$oid] = array_map(function ($e) { return array(null, $e); }, $actualData); } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->_originalEntityData[$oid]; $changeSet = array(); $entityIsDirty = false; foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (is_object($orgValue) && $orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) { $changeSet[$propName] = array($orgValue, $actualValue); } } if (isset($changeSet[$propName])) { if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; if ($assoc->isOneToOne() && $assoc->isOwningSide) { $entityIsDirty = true; } else { if ($orgValue instanceof PersistentCollection) { // A PersistentCollection was de-referenced, so delete it. if (!in_array($orgValue, $this->_collectionDeletions, true)) { $this->_collectionDeletions[] = $orgValue; } } } } else { $entityIsDirty = true; } } } if ($changeSet) { if ($entityIsDirty) { $this->_entityUpdates[$oid] = $entity; } $this->_entityChangeSets[$oid] = $changeSet; $this->_originalEntityData[$oid] = $actualData; } } }
/** * Computes the changes that happened to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ public function computeChangeSet(Mapping\ClassMetadata $class, $entity) { if (!$class->isInheritanceTypeNone()) { $class = $this->_em->getClassMetadata(get_class($entity)); } $oid = spl_object_hash($entity); $actualData = array(); foreach ($class->reflFields as $name => $refProp) { if (!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) { $actualData[$name] = $refProp->getValue($entity); } if ($class->isCollectionValuedAssociation($name) && $actualData[$name] !== null && !$actualData[$name] instanceof PersistentCollection) { // If $actualData[$name] is not a Collection then use an ArrayCollection. if (!$actualData[$name] instanceof Collection) { $actualData[$name] = new ArrayCollection($actualData[$name]); } $assoc = $class->associationMappings[$name]; // Inject PersistentCollection $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), $actualData[$name]); $coll->setOwner($entity, $assoc); $coll->setDirty(!$coll->isEmpty()); $class->reflFields[$name]->setValue($entity, $coll); $actualData[$name] = $coll; } } if (!isset($this->_originalEntityData[$oid])) { // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). // These result in an INSERT. $this->_originalEntityData[$oid] = $actualData; $this->_entityChangeSets[$oid] = array_map(function ($e) { return array(null, $e); }, $actualData); } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->_originalEntityData[$oid]; $changeSet = array(); $entityIsDirty = false; foreach ($actualData as $propName => $actualValue) { $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null; if (is_object($orgValue) && $orgValue !== $actualValue) { $changeSet[$propName] = array($orgValue, $actualValue); } else { if ($orgValue != $actualValue || $orgValue === null ^ $actualValue === null) { $changeSet[$propName] = array($orgValue, $actualValue); } } if (isset($changeSet[$propName])) { if (isset($class->associationMappings[$propName])) { $assoc = $class->associationMappings[$propName]; if ($assoc->isOneToOne()) { if ($assoc->isOwningSide) { $entityIsDirty = true; } if ($actualValue === null && $assoc->orphanRemoval) { $this->scheduleOrphanRemoval($orgValue); } } else { if ($orgValue instanceof PersistentCollection) { // A PersistentCollection was de-referenced, so delete it. if (!in_array($orgValue, $this->_collectionDeletions, true)) { $this->_collectionDeletions[] = $orgValue; } } } } else { $entityIsDirty = true; } } } if ($changeSet) { $this->_entityChangeSets[$oid] = $changeSet; $this->_originalEntityData[$oid] = $actualData; if ($entityIsDirty) { $this->_entityUpdates[$oid] = $entity; } } } // Look for changes in associations of the entity foreach ($class->associationMappings as $assoc) { $val = $class->reflFields[$assoc->sourceFieldName]->getValue($entity); if ($val !== null) { $this->_computeAssociationChanges($assoc, $val); } } }
/** * Computes the changes that happened to a single entity. * * Modifies/populates the following properties: * * {@link _originalEntityData} * If the entity is NEW or MANAGED but not yet fully persisted (only has an id) * then it was not fetched from the database and therefore we have no original * entity data yet. All of the current entity data is stored as the original entity data. * * {@link _entityChangeSets} * The changes detected on all properties of the entity are stored there. * A change is a tuple array where the first entry is the old value and the second * entry is the new value of the property. Changesets are used by persisters * to INSERT/UPDATE the persistent entity state. * * {@link _entityUpdates} * If the entity is already fully MANAGED (has been fetched from the database before) * and any changes to its properties are detected, then a reference to the entity is stored * there to mark it for an update. * * {@link _collectionDeletions} * If a PersistentCollection has been de-referenced in a fully MANAGED entity, * then this collection is marked for deletion. * * @ignore * @internal Don't call from the outside. * @param ClassMetadata $class The class descriptor of the entity. * @param object $entity The entity for which to compute the changes. */ public function computeChangeSet(ClassMetadata $class, $entity) { $oid = spl_object_hash($entity); if (isset($this->readOnlyObjects[$oid])) { return; } if (!$class->isInheritanceTypeNone()) { $class = $this->em->getClassMetadata(get_class($entity)); } // Fire PreFlush lifecycle callbacks if (isset($class->lifecycleCallbacks[Events::preFlush])) { $class->invokeLifecycleCallbacks(Events::preFlush, $entity); } $actualData = array(); foreach ($class->reflFields as $name => $refProp) { $value = $refProp->getValue($entity); if ($class->isCollectionValuedAssociation($name) && $value !== null && !$value instanceof PersistentCollection) { // If $value is not a Collection then use an ArrayCollection. if (!$value instanceof Collection) { $value = new ArrayCollection($value); } $assoc = $class->associationMappings[$name]; // Inject PersistentCollection $value = new PersistentCollection($this->em, $this->em->getClassMetadata($assoc['targetEntity']), $value); $value->setOwner($entity, $assoc); $value->setDirty(!$value->isEmpty()); $class->reflFields[$name]->setValue($entity, $value); $actualData[$name] = $value; continue; } if ((!$class->isIdentifier($name) || !$class->isIdGeneratorIdentity()) && $name !== $class->versionField) { $actualData[$name] = $value; } } if (!isset($this->originalEntityData[$oid])) { // Entity is either NEW or MANAGED but not yet fully persisted (only has an id). // These result in an INSERT. $this->originalEntityData[$oid] = $actualData; $changeSet = array(); foreach ($actualData as $propName => $actualValue) { if (!isset($class->associationMappings[$propName])) { $changeSet[$propName] = array(null, $actualValue); continue; } $assoc = $class->associationMappings[$propName]; if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) { $changeSet[$propName] = array(null, $actualValue); } } $this->entityChangeSets[$oid] = $changeSet; } else { // Entity is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data $originalData = $this->originalEntityData[$oid]; $isChangeTrackingNotify = $class->isChangeTrackingNotify(); $changeSet = $isChangeTrackingNotify && isset($this->entityChangeSets[$oid]) ? $this->entityChangeSets[$oid] : array(); foreach ($actualData as $propName => $actualValue) { // skip field, its a partially omitted one! if (!(isset($originalData[$propName]) || array_key_exists($propName, $originalData))) { continue; } $orgValue = $originalData[$propName]; // skip if value havent changed if ($orgValue === $actualValue) { continue; } // if regular field if (!isset($class->associationMappings[$propName])) { if ($isChangeTrackingNotify) { continue; } $changeSet[$propName] = array($orgValue, $actualValue); continue; } $assoc = $class->associationMappings[$propName]; if ($orgValue instanceof PersistentCollection) { // A PersistentCollection was de-referenced, so delete it. $coid = spl_object_hash($orgValue); if (isset($this->collectionDeletions[$coid])) { continue; } $this->collectionDeletions[$coid] = $orgValue; $changeSet[$propName] = $orgValue; // Signal changeset, to-many assocs will be ignored. continue; } if ($assoc['type'] & ClassMetadata::TO_ONE) { if ($assoc['isOwningSide']) { $changeSet[$propName] = array($orgValue, $actualValue); } if ($orgValue !== null && $assoc['orphanRemoval']) { $this->scheduleOrphanRemoval($orgValue); } } } if ($changeSet) { $this->entityChangeSets[$oid] = $changeSet; $this->originalEntityData[$oid] = $actualData; $this->entityUpdates[$oid] = $entity; } } // Look for changes in associations of the entity foreach ($class->associationMappings as $field => $assoc) { if (($val = $class->reflFields[$field]->getValue($entity)) !== null) { $this->computeAssociationChanges($assoc, $val); } } }