Capturing Flushed Entities Within The PostFlush Event Subscriber

A colleague and I recently ran into an interesting scenario when using Doctrine, a data mapper object-relational mapping library. As it turns out, you cannot natively get a list of flushed entities within the postFlush event subscriber method. But with a small amount of code, it can be achieved.

Overview

Doctrine tracks all changes made to entities within the identity map throughout the execution of the process. All these changes are stored inside the unit of work singleton and then, when we flush all the changes, Doctrine will automatically start a transaction, construct all the necessary SQL, and essentially persist all the changes tracked in the unit of work within the database.

This works really well, and also allows you to use key constraints in the database as a final stop-gap to integrity violations. Examples are uniqueness constraints on columns as well as composite unique indices.

When the unit of work is flushed, Doctrine can invoke any user-defined onFlush event handlers. In our scenario, we wanted to publish all the changesets within the unit of work down a web socket for various subscribers to consume down the line, and we first put this in an onFlush event handler.

The problem arose when a uniqueness constraint was violated – this happens during the flush event but too late in the process for our onFlush event handler to capture it, and the stack unwound due to the exception that was thrown. As such, we ended up with dirty, uncommitted changesets being published down the web socket despite the database rejecting the change and rolling back the transaction.

The fix

Our first guess was to use the postFlush even hander, but as of the latest version of Doctrine, it’s not possible to obtain an array of entities that were flushed. This was a bit of a problem as we needed this list for our socket subscribers.

It turns out though the fix was quite easy: construct an event subscriber that subscribes to both the onFlush and the postFlush events. We first store the entities in the onFlush, and then retrieve them during the postFlush, noting that during a deletion the entities are actually removed. We use a private field inside the instance to store the state. Below is the sample code:

Doctrine code showing how to use postFlush and onFlush together

Photo by Marc-Olivier Jodoin on Unsplash

Back to Blog