Jorgo Miridis
Jorgo Miridis

Reputation: 37

Symfony circular reference with EventListener that depends on a service that requires EntityManager

For simplicity, I have a service 'TenantContext', that retrieves the current tenant from the database on each request and therefore requires the EntityManager.

Furthermore I want to associate all Entities that have a ManyToOne relationship with Tenant automatically just before a flush happens, so I implemented a TenantListener that will detect those entities and do the association (setTenant()). To do that, the TenantListener requires the TenantContext.

Here is my services.yml

services:
    tenant.service.tenant_context:
        class: TenantBundle\Context\TenantContext
        arguments: [ "@doctrine.orm.entity_manager" ]

    tenant.event_listener.tenant_listener:
        class: TenantBundle\EventListener\TenantListener
        arguments: [ "@tenant.service.tenant_context" ]
        tags:
            - { name: doctrine.event_listener, event: preFlush }

This configuration gives me a circular reference error:

  [Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
  Circular reference detected for service "doctrine.dbal.default_connection", path: "doctrine.dbal.default_connection".

If I understand correctly, by tagging the EntityListener as a 'doctrine.event_listener' I am making the Doctrine EntityManager dependent on the TenantListener which depends on the TenantContext which again depends on the Doctrine EntityManager, etc.

I have looked into a similar post Symfony Circular Reference Exception for Doctrine onFlush Event Listener Service but my situation is somehow different. The TenantContext is used in many different ways besides from the TenantListener and I can't always pass the Doctrine EntityManager to get the current tenant.

I can't see a solution to break this circle. How can I resolve this?

Upvotes: 1

Views: 1098

Answers (1)

W0rma
W0rma

Reputation: 309

You may do not want to inject the EntityManager but the Doctrine Registry to your tenant.service.tenant_context service:

services:
    tenant.service.tenant_context:
        class: TenantBundle\Context\TenantContext
        arguments: [ "@doctrine" ]

Example on how to access the EntityManager instance in your TenantBundle\Context\TenantContext class:

namespace TenantBundle\Context

use Symfony\Bridge\Doctrine\RegistryInterface;

class TenantContext
{
    protected $doctrine;

    public function __construct(RegistryInterface $doctrine)
    {
        $this->doctrine = $doctrine;
    }

    public function getEntityManager()
    {
        return $this->doctrine->getEntityManager();
    } 
}

Upvotes: 4

Related Questions