Search code examples
phpmongodbsymfonydoctrinedoctrine-odm

Doctrine MongoDB Entity persisting don't work


In EventSubscriber i want to persist a history Version from Asset. The Handler prePersist work as expected but preUpdate dont work. Same Code but dont persist Document in database. No Error is thrown and code gets called.

Work:

public function prePersist(LifecycleEventArgs $eventArgs) {
        if ($eventArgs->getDocument() instanceof Asset) {
            $historyAsset = new HistoryAsset();
            $historyAsset->setAction('CREATE');
            $historyAsset->setData($this->normalizer->normalize($eventArgs->getObject(),null, [
                AbstractNormalizer::CALLBACKS => [
                    'typ' => function(AssetTyp $typ) {
                        return $typ->getName();
                    },
                ],
            ]));
            $historyAsset->setAsset($eventArgs->getDocument());
            $this->documentManager->persist($historyAsset);
        }
    }

Dont work:

public function preUpdate(LifecycleEventArgs $eventArgs): void {
        if ($eventArgs->getDocument() instanceof Asset) {
            //$changeset = $eventArgs->getDocumentManager()->getUnitOfWork()->getDocumentChangeSet($eventArgs->getDocument());

            $historyAsset1 = new HistoryAsset();
            $historyAsset1->setAction('UPDATE');
            $historyAsset1->setData([]);
            //$historyAsset->setData($this->normalizer->normalize($changeset));
            //$historyAsset->setAsset($eventArgs->getDocument());
            $this->documentManager->persist($historyAsset1);
            //dump($this->documentManager->getUnitOfWork()->isScheduledForInsert($historyAsset1)); // true
            //dump($historyAsset1); // correct
        }
    }`

Document:

#[Document(collection: 'History')]
class HistoryAsset
{
    #[Id(strategy: 'AUTO')]
    protected string $id;
    #[ReferenceOne(storeAs: "id", targetDocument: Asset::class)]
    protected Asset|null $asset = null;
    #[Field]
    protected string $action = '';
    #[Field]
    protected array $data = [];

    public function __construct()
    {
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getAction(): string
    {
        return $this->action;
    }

    public function setAction(string $action): void
    {
        $this->action = $action;
    }

    public function getData(): array
    {
        return $this->data;
    }

    public function setData(array $data): void
    {
        $this->data = $data;
    }

    public function getAsset(): Asset|null
    {
        return $this->asset;
    }

    public function setAsset(Asset $asset): void
    {
        $this->asset = $asset;
    }
}

I dont have an idea where to.search...

Thank you

.

Document isnt persisted to DB


Solution

  • This has to do with the order operations are executed in the UnitOfWork, and when you schedule documents for insertion/update.

    In general, UnitOfWork persists new documents first and executes updates later. During this update process, the preUpdate and postUpdate events are triggered. Since insertions have already been handled at this point, any new documents scheduled for insertion in preUpdate or postUpdate will not be stored.

    Even when doing this in prePersist, there is no guarantee that a document will be written. Since you are modifying the commit chain after it has been started, ODM may have already handled insertions for the class that you're adding a new document for, in which case the new document won't be inserted.

    Note that this is why it is not recommended to couple business logic to the event system; it is designed as an extension point and requires significant "inside knowledge" about how UnitOfWork does its job. If you do want to use the event system for this purpose, all changes to the commit set (e.g. scheduling new documents for insertion or update) must be made in the onFlush event. Only then will you have a guarantee that your newly scheduled changes will be persisted to the database.