docs(articles): English translation of From Magento to Symfony
en/articles/magento-to-symfony/quality/traps.md
new file +81
ā0
1
+
# Pitfalls
2
+
3
+
Where Magento experience lets you down most often. Red blocks are pitfalls that corrupt data, yellow ones cost you time.
4
+
5
+
## flush() writes everything
6
+
7
+
:::danger subtitle="Data"
8
+
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.
9
+
:::
10
+
11
+
## State in service properties
12
+
13
+
:::danger subtitle="Data"
14
+
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.
15
+
:::
16
+
17
+
## EntityManager is closed
18
+
19
+
:::danger subtitle="Data"
20
+
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()`.
21
+
:::
22
+
23
+
## N+1 on lazy associations
24
+
25
+
:::warning subtitle="Performance"
26
+
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.
27
+
:::
28
+
29
+
## Changes inside an object go unnoticed
30
+
31
+
:::warning subtitle="Data"
32
+
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.
33
+
:::
34
+
35
+
## The inverse side isn't persisted
36
+
37
+
:::warning subtitle="Data"
38
+
In a bidirectional association, only the owning side is written to the database. Add helpers that update both sides.
39
+
:::
40
+
41
+
## cache:clear doesn't clear data
42
+
43
+
:::warning subtitle="Time"
44
+
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.
45
+
:::
46
+
47
+
## $container->get() doesn't work
48
+
49
+
:::warning subtitle="Time"
50
+
Services are private, so the ObjectManager approach is impossible. If you need to pick a service at runtime, use `#[AutowireLocator]`.
51
+
:::
52
+
53
+
## An unused service disappears
54
+
55
+
:::warning subtitle="Time"
56
+
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.
57
+
:::
58
+
59
+
## Listener order matters
60
+
61
+
:::warning subtitle="Security"
62
+
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`.
63
+
:::
64
+
65
+
## Old code in workers
66
+
67
+
:::warning subtitle="Deployment"
68
+
A worker keeps the code in memory. After deployment run `messenger:stop-workers`, otherwise messages are handled by the previous version.
69
+
:::
70
+
71
+
## Secrets in .env
72
+
73
+
:::warning subtitle="Security"
74
+
`.env` is committed, so it holds only default values for dev. Real keys go into the server's environment variables or into `secrets:set`.
75
+
:::
76
+
77
+
## Porting Magento layers one to one
78
+
79
+
:::warning subtitle="Architecture"
80
+
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.
81
+
:::