Also ... was tun, wenn wir einen Änderungssatz außerhalb des Doctrine-Lebenszyklus finden möchten? Wie in meinem Kommentar zu @Ocramius 'Beitrag oben erwähnt, ist es möglicherweise möglich, eine "schreibgeschützte" Methode zu erstellen, die nicht mit der tatsächlichen Doctrine-Persistenz in Konflikt gerät, sondern dem Benutzer einen Überblick darüber gibt, was sich geändert hat.
Hier ist ein Beispiel dafür, woran ich denke ...
public static function diffDoctrineObject(EntityManager $em, $entity) {
$uow = $em->getUnitOfWork();
$class = $em->getClassMetadata(get_class($entity));
$oid = spl_object_hash($entity);
$entityChangeSets = array();
if ($uow->isReadOnly($entity)) {
return null;
}
if ( ! $class->isInheritanceTypeNone()) {
$class = $em->getClassMetadata(get_class($entity));
}
$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 instanceof Collection) {
$value = new ArrayCollection($value);
}
$assoc = $class->associationMappings[$name];
$value = new PersistentCollection(
$em, $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;
}
}
$originalEntityData = $uow->getOriginalEntityData($entity);
if (empty($originalEntityData)) {
$originalEntityData = $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);
}
}
$entityChangeSets[$oid] = $changeSet;
} else {
$originalData = $originalEntityData;
$isChangeTrackingNotify = $class->isChangeTrackingNotify();
$changeSet = $isChangeTrackingNotify ? $uow->getEntityChangeSet($entity) : array();
foreach ($actualData as $propName => $actualValue) {
if ( ! (isset($originalData[$propName]) || array_key_exists($propName, $originalData))) {
continue;
}
$orgValue = $originalData[$propName];
if ($orgValue === $actualValue) {
continue;
}
if ( ! isset($class->associationMappings[$propName])) {
if ($isChangeTrackingNotify) {
continue;
}
$changeSet[$propName] = array($orgValue, $actualValue);
continue;
}
$assoc = $class->associationMappings[$propName];
if ($actualValue instanceof PersistentCollection) {
$owner = $actualValue->getOwner();
if ($owner === null) {
$actualValue->setOwner($entity, $assoc);
} else if ($owner !== $entity) {
if (!$actualValue->isInitialized()) {
$actualValue->initialize();
}
$newValue = clone $actualValue;
$newValue->setOwner($entity, $assoc);
$class->reflFields[$propName]->setValue($entity, $newValue);
}
}
if ($orgValue instanceof PersistentCollection) {
$changeSet[$propName] = $orgValue;
continue;
}
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($assoc['isOwningSide']) {
$changeSet[$propName] = array($orgValue, $actualValue);
}
}
}
if ($changeSet) {
$entityChangeSets[$oid] = $changeSet;
}
}
return $entityChangeSets[$oid];
}
Es wird hier als statische Methode formuliert, könnte aber zu einer Methode in UnitOfWork werden ...?
Ich bin nicht mit allen Interna von Doctrine auf dem Laufenden, habe also möglicherweise etwas übersehen, das einen Nebeneffekt hat oder einen Teil der Funktionsweise dieser Methode missverstanden hat, aber ein (sehr) schneller Test scheint mir die erwarteten Ergebnisse zu liefern sehen.
Ich hoffe das hilft jemandem!