Pitfalls

← Pitfalls

Ashterix

feat(seo): og:image size for front matter images; short titles and explicit descriptions for From Magento to Symfony

en/articles/magento-to-symfony/quality/traps.md +2 −1

Pitfalls

Where Magento experience lets you down most often. Red blocks are pitfalls that corrupt data, yellow ones cost you time.

flush() writes everything

Data

Not just "this" entity, but every entity changed in the UnitOfWork. flush($entity) was removed in ORM 3. Keep flush at the entry point and don't modify entities you don't intend to save.

State in service properties

Data

Services are shared, and a Messenger worker lives for hours and handles thousands of messages. A cache, the current user or an order stored in a service property leaks between messages. Implement ResetInterface or don't keep state.

EntityManager is closed

Data

After an exception during flush, the EntityManager is closed and subsequent operations fail. Don't catch the exception to carry on with the same EM. In Messenger workers the Doctrine middleware takes care of this; in your own loops use ManagerRegistry::resetManager().

N+1 on lazy associations

Performance

The habit of "load a collection, then call getX() in a loop" generates a query per iteration here. Watch the query counter in the profiler and use a fetch join.

Changes inside an object go unnoticed

Data

Doctrine compares values, not the contents of objects. $this->date->modify('+1 day') on a mutable DateTime won't be saved. Use DateTimeImmutable and assign a new value; the same goes for arrays of objects in JSON columns.

The inverse side isn't persisted

Data

In a bidirectional association, only the owning side is written to the database. Add helpers that update both sides.

cache:clear doesn't clear data

Time

The system cache and cache pools are different things. Stale data in Redis stays after cache:clear; use cache:pool:clear or tag-based invalidation for it.

$container->get() doesn't work

Time

Services are private, so the ObjectManager approach is impossible. If you need to pick a service at runtime, use #[AutowireLocator].

An unused service disappears

Time

The container removes private services that nobody injects. In tests, getContainer()->get() on such a service throws an error; this is not a config bug.

Listener order matters

Security

The router on kernel.request has priority 32, the firewall has 8. A listener with priority 20 runs before authentication and doesn't see the user. Check with debug:event-dispatcher.

Old code in workers

Deployment

A worker keeps the code in memory. After deployment run messenger:stop-workers, otherwise messages are handled by the previous version.

Secrets in .env

Security

.env is committed, so it holds only default values for dev. Real keys go into the server's environment variables or into secrets:set.

Porting Magento layers one to one

Architecture

An Api/Data interface, a Repository wrapper around the repository, a Management service for every entity: this is ceremony with no benefit in an application you own. Add a layer when there is a real reason for it.