Alfred Wallace
Alfred Wallace

Reputation: 173

Cannot fetch the Doctrine EntityManager with autowire in Symfony

Well, I'm going crazy, there's something I don't get with the Symfony (3.3) autowiring stuff. I've read those resources : http://symfony.com/doc/current/service_container.html http://symfony.com/doc/current/service_container/3.3-di-changes.html and others that I can't post because I need 10 reputation. I've also read many articles/stackoverflow posts, but with no luck.

I've tried to set up my services, and it almost works, it's just that the EntityManager is null, so I can't call ->getRepository() on it. The error happens on the line like this in my factory:

$databaseTournament = $this->em->getRepository('AppBundle:Tournament')
    ->find($tournament);

"Call to a member function getRepository() on null"

It seems to find the services correctly but never injects the EntityManager in the factory. I've tried to configure things explicitely in the services.yml, but I never managed to make it work.

I'm trying to figure it ou since yesterday, but I'm getting really confused. Could you help me ? Thank you in advance ! :)

Here are my files :

services.yml:

parameters:
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false
    AppBundle\:
        resource: '../../src/AppBundle/*'
        exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
    AppBundle\Controller\:
        resource: '../../src/AppBundle/Controller'
        public: true
        tags: ['controller.service_arguments']

EntryFactory:

<?php
namespace AppBundle\Factory;

use AppBundle\Entity\Entry;
use AppBundle\Entity\Team;
use AppBundle\Entity\Tournament;
use AppBundle\Exception\InvalidEntryArgumentException;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityNotFoundException;

class EntryFactory
{
    private $em;

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

    public function createEntry($tournament, $team, $eliminated = false)
    {
        $entry = new Entry();

        if ($tournament instanceof Tournament) {
            $entry->setTournament($tournament);
        }
        elseif (is_int($tournament) && $tournament >= 0) {
            $databaseTournament = $this->em->getRepository('AppBundle:Tournament')->find($tournament);
            if (is_null($databaseTournament)) {
                throw new EntityNotFoundException('Tournament (id:'.$tournament.') not found.');
            }
            else {
                $entry->setTournament($databaseTournament);
            }
        }
        else {
            throw new InvalidEntryArgumentException('Could not determine the Tournament argument.');
        }

        if ($team instanceof Team) {
            $entry->setTeam($team);
        }
        elseif (is_int($team) && $team >= 0) {
            $databaseTeam = $this->em->getRepository('AppBundle:Team')->find($team);
            if (is_null($databaseTeam)) {
                throw new EntityNotFoundException('Team (id:'.$team.') not found.');
            }
            else {
                $entry->setTeam($databaseTeam);
            }
        }
        else {
             throw new InvalidEntryArgumentException('Could not determine the Team argument.');
        }


        $entry->setEliminated($eliminated);
        return $entry;
    }
}

EntryController:

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Entry;
use Doctrine\ORM\EntityNotFoundException;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\ConstraintViolationList;
use AppBundle\Factory;

class EntryController extends FOSRestController
{
    /**
     * @ApiDoc(
     *     resource=true,
     *     section="Entries",
     *     description="Creates a new entry.",
     *     statusCodes={
     *          201="Returned when created.",
     *          400="Returned when a violation is raised by validation.",
     *          404="Returned when a team or a tournament is not found."
     *     }
     * )
     *
     * @Rest\Post(
     *     path="/entries",
     *     name="app_entry_create"
     * )
     * @Rest\View(statusCode=201)
     * @Rest\RequestParam(
     *     name="tournamentId",
     *     requirements="\d+",
     *     nullable=false,
     *     description="The id of the tournament in which the team enters."
     * )
     * @Rest\RequestParam(
     *     name="teamId",
     *     requirements="\d+",
     *     nullable=false,
     *     description="The id of the team entering the tournament."
     * )
     *
     * @param $tournamentId
     * @param $teamId
     * @param ConstraintViolationList $violationList
     * @param Factory\EntryFactory $entryFactory
     * @return Entry|View
     * @throws EntityNotFoundException
     * @internal param $id
     * @internal param Entry $entry
     */
    public function createEntryAction($tournamentId, $teamId, ConstraintViolationList $violationList,
                                      Factory\EntryFactory $entryFactory)
    {
        if (count($violationList)) {
            return $this->view($violationList, Response::HTTP_BAD_REQUEST);
        }
        $entry = $entryFactory->createEntry($tournamentId,$teamId);
        $em->persist($entry);
        $em->flush();
        return $entry;
    }
}

edit:

I must add that I've tried this and it doesn't work either:

in the services.yml:

AppBundle\Factory\EntryFactory:
    public: true
    arguments: ['@doctrine.orm.entity_manager']

in the EntryFactory:

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

edit 2:

I've also tried to disable everything new : still the same error

AppBundle\Factory\EntryFactory:
        autowire: false
        autoconfigure: false
        public: true
        arguments:
            $em: '@doctrine.orm.entity_manager'

Same with disabling the folder in the default config :

AppBundle\:
        resource: '../../src/AppBundle/*'
        exclude: '../../src/AppBundle/{Entity,Repository,Tests,Factory}'

Upvotes: 3

Views: 6930

Answers (1)

Will B.
Will B.

Reputation: 18416

An alternative to force autowiring Dependency Injection in Symfony 3.3 or later, without having to explicitly define the entity manager for each service. You can add an alias of the desired entity manager service.

#app/config/services.yml
services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    AppBundle\:
        resource: '../../src/AppBundle/*'
        exclude: '../../src/AppBundle/{Entity,Repository,Tests}'

    AppBundle\Controller\:
        resource: '../../src/AppBundle/Controller/*'
        public: true
        tags: ['controller.service_arguments']

    Doctrine\ORM\EntityManagerInterface: '@doctrine.orm.entity_manager'

   #custom services below here
   #...

This will then allow Symfony to determine Doctrine\ORM\EntityManagerInterface as the service you want to inject.

use Doctrine\ORM\EntityManagerInterface;

class EntryFactory
{
    private $em;

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

End result will be in your services configuration as:

return $this->services['AppBundle\Factory\EntryFactory'] = new \AppBundle\Factory\EntryFactory(${($_ = isset($this->services['doctrine.orm.default_entity_manager']) ? $this->services['doctrine.orm.default_entity_manager'] : $this->load('getDoctrine_Orm_DefaultEntityManagerService.php')) && false ?: '_'});

Note: Be sure to warmup your Symfony cache to ensure new service declarations are created.

php composer.phar install

or

php composer.phar install --no-scripts
php bin/console --env=dev cache:clear --no-warmup
php bin/console --env=dev cache:warmup

Upvotes: 1

Related Questions