はじめに、何が問題か
下記のissueが分かりやすいので、引用して説明します。
services:
acme.invoice.listener:
class: %acme.invoice.listener.class%
arguments:
- "@acme.invoice.manager"
tags:
- { name: doctrine.orm.entity_listener }
acme.invoice.manager:
class: %acme.invoice.manager.class%
arguments:
- "@doctrine.orm.entity_manager"
上記のようなサービス定義をしていると、ORMException
が発生します。
[Doctrine\ORM\ORMException]
It's a requirement to specify a Metadata Driver and pass it to Doctrine\ORM\Configuration::setMetadataDriverImpl().
今までどうしてきたか
このissueは、2014-06-17に作成されました。
10日後の2014-06-27には、下記のように、コメントが付いています。
We passed the service_container to the listener.
It's not the best solution but we don't have time to find a better one.
私たちはサービスコンテナをリスナーに渡すようにした。
決して最良の解決策ではないが、これよりいい方法を探す時間がない。
つまりは、EntityListener
には、具体的なサービスではなく、サービスコンテナそのものを渡すというバッドノウハウが使われてきたようです。
2年のときを経て、いよいよ解決に
2015-07-06に、この問題を解決すべく、下記Pull-Requestが作成されました。
そして、様々な議論・レビューの結果、2016-11-19にmasterブランチにマージされました。
doctrine/doctrine-bundle
の1.6.5
にて上記PRが取り込まれています。
結論、こうすれば解決する
-
doctrine/doctrine-bundle
を1.6.5
以上に更新する -
EntityListener
のtags
にlazy: true
を付け加える
services:
acme.invoice.listener:
class: %acme.invoice.listener.class%
arguments:
- "@acme.invoice.manager"
tags:
- { name: doctrine.orm.entity_listener, lazy: true }
acme.invoice.manager:
class: %acme.invoice.manager.class%
arguments:
- "@doctrine.orm.entity_manager"
おわりに
サービスにサービスコンテナをインジェクトするのは、アンチパターンだということをチーム内で声高に言ったら、この問題を踏んでしまいました。
くじけずに調べて解決できて良かったです。
同じ問題にぶち当たった人が見てくれるといいなと思っています。
ではでは。