Is there a way to inject EntityManager into a service
Asked Answered
S

4

13

While using Symfony 3.3, I am declaring a service like this:

class TheService implements ContainerAwareInterface
{
    use ContainerAwareTrait;
    ...
}

Inside each action where I need the EntityManager, I get it from the container:

$em = $this->container->get('doctrine.orm.entity_manager');

This is a bit annoying, so I'm curious whether Symfony has something that acts like EntityManagerAwareInterface.

Stockbreeder answered 28/6, 2017 at 18:10 Comment(1)
a quick google search would have been faster than actually typing this questionInterdisciplinary
C
42

Traditionally, you would have created a new service definition in your services.yml file set the entity manager as argument to your constructor

app.the_service:
    class: AppBundle\Services\TheService
    arguments: ['@doctrine.orm.entity_manager']

More recently, with the release of Symfony 3.3, the default symfony-standard-edition changed their default services.yml file to default to using autowire and add all classes in the AppBundle to be services. This removes the need for adding the custom service and using a type hint in your constructor will automatically inject the right service.

Your service class would then look like the following:

use Doctrine\ORM\EntityManagerInterface;

class TheService
{
    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    // ...
}

For more information about automatically defining service dependencies, see https://symfony.com/doc/current/service_container/autowiring.html

The new default services.yml configuration file is available here: https://github.com/symfony/symfony-standard/blob/3.3/app/config/services.yml

Councilman answered 28/6, 2017 at 18:35 Comment(3)
Thanks. Is there an easy way to inject Repositories instead of EntityManager?Stockbreeder
@Stockbreeder Autowiring works for the repositories tooHandtohand
Note that, at least in my case, the important thing is to autowire EntityManagerInterface instead of EntityManager directly.Goering
D
2

Sometimes I inject the EM into a service on the container like this in services.yml:

 application.the.service:
      class: path\to\te\Service
      arguments:
        entityManager: '@doctrine.orm.entity_manager'

And then on the service class get it on the __construct method. Hope it helps.

Devitrify answered 28/6, 2017 at 18:18 Comment(1)
Small note: In the parameter list of the __construct method there must be a parameter named $entityManager, otherwise Symfony don't know which one to use for argument entityManager.Tibbitts
D
0

I ran into the same issue and solved it by editing the migration code.

I replaced

$this->addSql('ALTER TABLE user ADD COLUMN name VARCHAR(255) NOT NULL');

by

$this->addSql('ALTER TABLE user ADD COLUMN name VARCHAR(255) NOT NULL DEFAULT "-"');

I don't know why bin/console make:entity doesn't prompt us to provide a default in those cases. Django does it and it works well.

Depress answered 14/3, 2021 at 13:29 Comment(0)
H
0

So I wanted to answer your subquestion:

This is a bit annoying, so I'm curious whether Symfony has something that acts like EntityManagerAwareInterface.

And I think there is a solution to do so (I use it myself). The idea is that you slightly change your kernel so tha it checks for all services which implement the EntityManagerAwareInterface and injects it for them.

You can also add write an EntityManagerAwareTrait that implements the $entityManager property and the setEntityManager()setter. The only thing left after that is to implement/use the interface/trait couple the way you would do for the Logger for example.

(you could have done this through a compiler pass as well).

<?php
// src/Kernel.php

namespace App;

use App\Entity\EntityManagerAwareInterface;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use function array_key_exists;

class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;

    public function process(ContainerBuilder $container): void
    {
        $definitions = $container->getDefinitions();
        foreach ($definitions as $definition) {
            if (!$this->isAware($definition, EntityManagerAwareInterface::class)) {
                continue;
            }
            $definition->addMethodCall('setEntityManager', [$container->getDefinition('doctrine.orm.default_entity_manager')]);
        }
    }

    private function isAware(Definition $definition, string $awarenessClass): bool
    {
        $serviceClass = $definition->getClass();
        if ($serviceClass === null) {
            return false;
        }
        $implementedClasses = @class_implements($serviceClass, false);
        if (empty($implementedClasses)) {
            return false;
        }
        if (array_key_exists($awarenessClass, $implementedClasses)) {
            return true;
        }

        return false;
    }
}

The interface:

<?php
declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\EntityManagerInterface;

interface EntityManagerAwareInterface
{
    public function setEntityManager(EntityManagerInterface $entityManager): void;
}

The trait:

<?php
declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\EntityManagerInterface;

trait EntityManagerAwareTrait
{
    /** @var EntityManagerInterface */
    protected $entityManager;

    public function setEntityManager(EntityManagerInterface $entityManager): void
    {
        $this->entityManager = $entityManager;
    }
}

And now you can use it:

    <?php
    
    // src/SomeService.php
    declare(strict_types=1);
    
    namespace App;
    
    use Exception;
    use App\Entity\EntityManagerAwareInterface;
    use App\Entity\Entity\EntityManagerAwareTrait;
    use App\Entity\Entity\User;
    
    class SomeService implements EntityManagerAwareInterface
    {
        use EntityManagerAwareTrait;

        public function someMethod()
        {    
           $users = $this->entityManager->getRepository(User::Class)->findAll();
           // ...
        }
    }
Hubey answered 19/5, 2022 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.