93 votes

Existe-t-il un moyen intégré d'obtenir tous les champs modifiés/mis à jour dans une entité Doctrine 2 ?

Supposons que je récupère une entité $e et modifier son état avec des setters :

$e->setFoo('a');
$e->setBar('b');

Est-il possible de récupérer un tableau des champs qui ont été modifiés ?

Dans le cas de mon exemple, je voudrais récupérer foo => a, bar => b en conséquence

PS : oui, je sais que je peux modifier tous les accesseurs et mettre en œuvre cette fonctionnalité manuellement, mais je cherche un moyen pratique de le faire.

164voto

Ocramius Points 10275

Vous pouvez utiliser Doctrine\ORM\EntityManager#getUnitOfWork pour obtenir un Doctrine\ORM\UnitOfWork .

Ensuite, il suffit de déclencher le calcul des changeset (fonctionne uniquement sur les entités gérées) via Doctrine\ORM\UnitOfWork#computeChangeSets() .

Vous pouvez également utiliser des méthodes similaires comme Doctrine\ORM\UnitOfWork#recomputeSingleEntityChangeSet(Doctrine\ORM\ClassMetadata $meta, $entity) si vous savez exactement ce que vous voulez vérifier sans itérer sur l'ensemble du graphe d'objets.

Après cela, vous pouvez utiliser Doctrine\ORM\UnitOfWork#getEntityChangeSet($entity) pour récupérer toutes les modifications apportées à votre objet.

Assemblage :

$entity = $em->find('My\Entity', 1);
$entity->setTitle('Changed Title!');
$uow = $em->getUnitOfWork();
$uow->computeChangeSets(); // do not compute changes if inside a listener
$changeset = $uow->getEntityChangeSet($entity);

Note. Si vous essayez d'obtenir les champs mis à jour dans un écouteur preUpdate ne recalculez pas l'ensemble des changements, car cela a déjà été fait. Il suffit d'appeler le getEntityChangeSet pour obtenir toutes les modifications apportées à l'entité.

Attention : Comme expliqué dans les commentaires, cette solution ne doit pas être utilisée en dehors des écouteurs d'événements de Doctrine. Cela briserait le comportement de Doctrine.

52voto

Mohamed Ramrami Points 1889

Vérifiez cette fonction publique (et non interne) :

$this->em->getUnitOfWork()->getOriginalEntityData($entity);

De la doctrine repo :

/**
 * Gets the original data of an entity. The original data is the data that was
 * present at the time the entity was reconstituted from the database.
 *
 * @param object $entity
 *
 * @return array
 */
public function getOriginalEntityData($entity)

Il suffit d'implémenter un toArray o serialize dans votre entité et faites une différence. Quelque chose comme ceci :

$originalData = $em->getUnitOfWork()->getOriginalEntityData($entity);
$toArrayEntity = $entity->toArray();
$changes = array_diff_assoc($toArrayEntity, $originalData);

7 votes

Comment appliquer cela à une situation où une entité est liée à une autre (peut être OneToOne) ? Dans ce cas, lorsque j'exécute getOriginalEntityData sur une entité de niveau supérieur, les données originales de ses entités liées ne sont pas vraiment originales mais plutôt mises à jour.

44voto

Slavik Derevianko Points 166

Grand panneau d'avertissement pour ceux qui veulent vérifier les changements sur l'entité en utilisant la méthode décrite ci-dessus.

$uow = $em->getUnitOfWork();
$uow->computeChangeSets();

El $uow->computeChangeSets() est utilisée en interne par la routine de persistance d'une manière qui rend la solution ci-dessus inutilisable. C'est également ce qui est écrit dans les commentaires de la méthode : @internal Don't call from the outside . Après avoir vérifié les modifications apportées aux entités avec $uow->computeChangeSets() le morceau de code suivant est exécuté à la fin de la méthode (pour chaque entité gérée) :

if ($changeSet) {
    $this->entityChangeSets[$oid]   = $changeSet;
    $this->originalEntityData[$oid] = $actualData;
    $this->entityUpdates[$oid]      = $entity;
}

El $actualData Le tableau contient les modifications actuelles des propriétés de l'entité. Dès que celles-ci sont écrites dans le tableau $this->originalEntityData[$oid] Ces modifications non encore persistées sont considérées comme les propriétés originales de l'entité.

Plus tard, lorsque le $em->persist($entity) est appelée pour sauvegarder les modifications apportées à l'entité, elle fait également intervenir la méthode $uow->computeChangeSets() mais il ne sera pas en mesure de trouver les modifications apportées à l'entité, car ces modifications non encore persistées sont considérées comme les propriétés originales de l'entité.

8voto

Omar Makled Points 372

Il retournera les changements

$entityManager->getUnitOfWork()->getEntityChangeSet($entity)

0 votes

C'est tellement évident.

5voto

manix Points 2396

Vous pouvez suivre les changements avec Notifier les politiques .

Premièrement, il met en œuvre le NotifyPropertyChanged interface :

/**
 * @Entity
 * @ChangeTrackingPolicy("NOTIFY")
 */
class MyEntity implements NotifyPropertyChanged
{
    // ...

    private $_listeners = array();

    public function addPropertyChangedListener(PropertyChangedListener $listener)
    {
        $this->_listeners[] = $listener;
    }
}

Ensuite, il suffit d'appeler le _onPropertyChanged sur chaque méthode qui modifie les données de votre entité comme ci-dessous :

class MyEntity implements NotifyPropertyChanged
{
    // ...

    protected function _onPropertyChanged($propName, $oldValue, $newValue)
    {
        if ($this->_listeners) {
            foreach ($this->_listeners as $listener) {
                $listener->propertyChanged($this, $propName, $oldValue, $newValue);
            }
        }
    }

    public function setData($data)
    {
        if ($data != $this->data) {
            $this->_onPropertyChanged('data', $this->data, $data);
            $this->data = $data;
        }
    }
}

9 votes

Des auditeurs à l'intérieur d'une entité ! C'est de la folie ! Sérieusement, la politique de suivi semble être une bonne solution, y a-t-il un moyen de définir des écouteurs en dehors de l'entité (j'utilise le DoctrineBundle de Symfony2).

0 votes

C'est la mauvaise solution. Vous devriez vous tourner vers les événements du domaine. github.com/gpslab/domain-event

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X