Retrieving a Doctrine entity's changeset after an update

Published on 2021-04-21 • Modified on 2021-04-21

In this snippet, we see how to retrieve a Doctrine entity's changeset after an update. This is essential to know when using Doctrine. Typically, after an update, you may have to trigger some processing depending on a modified state or value. One can raise an event that will trigger all the needed processes for your functional case. In the second part of the snippet, we see how to detect the entity's current state from the ORM's perspective.


<?php

declare(strict_types=1);

namespace App\Controller\Snippet;

use App\Data\ArticleData;
use App\Entity\Article;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\UnitOfWork;

/**
 * I am using a PHP trait to isolate each snippet in a file.
 * This code should be called from a Symfony controller extending AbstractController (as of Symfony 4.2)
 * or Symfony\Bundle\FrameworkBundle\Controller\Controller (Symfony <= 4.1).
 * Services are injected in the main controller constructor.
 *
 * @property ArticleData            $articleData
 * @property EntityManagerInterface $entityManager
 */
trait Snippet142Trait
{
    public function snippet142(): void
    {
        // Get the current snippet
        $snippet = $this->articleData->getArticleById(142);

        // Get the unit of work object
        $uow = $this->entityManager->getUnitOfWork();

        // Modify a field
        $snippet->setAuthor('Tintin'.random_int(1, 99));

        // Compute all changesets
        $uow->computeChangeSets();

        // Or for this entity only
        $uow->computeChangeSet($this->entityManager->getClassMetadata(Article::class), $snippet);

        // Then get it, we have the field modified as the key which has for value
        // an array with the old as first item and new value as the second item
        // try it!
        $changeset = $uow->getEntityChangeSet($snippet);

        var_dump($changeset);

        // And what is the state of this entity? 🧐
        switch ($uow->getEntityState($snippet)) {
            case UnitOfWork::STATE_MANAGED:
                echo 'Snippet is managed (already in DB).';
                break;
            case UnitOfWork::STATE_REMOVED:
                echo 'Snippet is going to be removed.';
                break;
            case UnitOfWork::STATE_DETACHED:
                echo 'Snippet is detached.';
                break;
            case UnitOfWork::STATE_NEW:
                echo 'Snippet is new.';
                break;
        }

        // That's it! 😁
    }
}

 Run this snippet  ≪ this.showUnitTest ? this.trans.hide_unit_test : this.trans.show_unit_test ≫  More on Stackoverflow   Read the doc  More on the web

  Work with me!

<?php

declare(strict_types=1);

namespace App\Tests\Integration\Controller\Snippets;

use App\Data\ArticleData;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

/**
 * @see Snippet142Trait
 */
final class Snippet142Test extends KernelTestCase
{
    private Registry $doctrine;
    private ArticleData $articleData;

    protected function setUp(): void
    {
        $this->doctrine = self::getContainer()->get('doctrine');
        $this->articleData = self::getContainer()->get(ArticleData::class);
    }

    /**
     * @see Snippet142Trait::snippet142
     */
    public function testSnippet142(): void
    {
        $manager = $this->doctrine->getManager();
        if (!$manager instanceof EntityManager) {
            throw new \RuntimeException('Houston, We Have a Problem. 💥');
        }

        $uow = $manager->getUnitOfWork();
        $snippet = $this->articleData->getArticleById(142);
        $snippet->setAuthor('Tintin');
        $uow->computeChangeSets();
        $changeset = $uow->getEntityChangeSet($snippet);

        self::assertIsArray($changeset);
        self::assertArrayHasKey('author', $changeset);
        self::assertSame('COil', $changeset['author'][0]);
        self::assertSame('Tintin', $changeset['author'][1]);
    }
}