Symfony2
Doctrine2

EntityListnerをサービスとして定義しているときに、EntityManagerをインジェクトしているサービスをインジェクトできない(解決済み)

More than 1 year has passed since last update.

はじめに、何が問題か

下記の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-bundle1.6.5にて上記PRが取り込まれています。

結論、こうすれば解決する

  • doctrine/doctrine-bundle1.6.5以上に更新する
  • EntityListenertagslazy: 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"

おわりに

サービスにサービスコンテナをインジェクトするのは、アンチパターンだということをチーム内で声高に言ったら、この問題を踏んでしまいました。
くじけずに調べて解決できて良かったです。
同じ問題にぶち当たった人が見てくれるといいなと思っています。
ではでは。