David Patterson
David Patterson

Reputation: 1910

Symfony autowiring is not picking up the services in a specified path

Trying to upgrade an existing Symfony 3.3.2 project to use autowiring.
Followed the instructions here: https://symfony.com/doc/current/service_container/3.3-di-changes.html#content_wrapper.

What is not working for me is being able to set all services in a directory to public.

Important: My services.yml file is in src/SiteBundle/Resources/config, not app/config.

# src/SiteBundle/Resources/config/services.yml
services:
  _defaults: 
    autowire: true
    autoconfigure: true
    public: false

  SiteBundle\Service:
    resource: '../../Service'
    public: true

  CustomerOrderMasterRepository:
    class: Doctrine\ORM\EntityRepository
    factory: ["@doctrine.orm.entity_manager", get_repository]
    arguments:
      - SiteBundle\Entity\CustomerMaster

  SiteBundle\Service\CustomersService:
    arguments:
      - '@doctrine.orm.entity_manager'
      - '@CustomerMasterRepository'

If I then do a console debug:container | grep -i service, it shows only the output header and the Symfony service container. None of the classes in the src/SiteBundle/Service directory are being picked up.

Symfony Container Public Services Service ID
Class name service_container
Symfony\Component\DependencyInjection\ContainerInterface

If I add --show-private, then they are displayed (along with a lot of other stuff).

Symfony Container Public and Private Services Service ID
Class name
1_bcf140bb848ef41617942628c8525b4872574e826d00fee6aaabbf2ede89fbb8
Symfony\Component\DependencyInjection\ServiceLocator
Psr\Container\ContainerInterface
alias for "service_container"
SiteBundle\Service\CustomerOrdersService
SiteBundle\Service\CustomerOrdersService
SiteBundle\Service\CustomersService
SiteBundle\Service\CustomersService
SiteBundle\Service\InventoryItemsService
SiteBundle\Service\InventoryItemsService
SiteBundle\Service\VendorOrdersService
SiteBundle\Service\VendorOrdersService
SiteBundle\Service\VendorsService
SiteBundle\Service\VendorsService
SiteBundle\Service\WorkOrdersService
SiteBundle\Service\WorkOrdersService
Symfony\Component\DependencyInjection\ContainerInterface
alias for "service_container" argument_resolver.service
Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver routing.loader.service
Symfony\Component\Routing\Loader\DependencyInjection\ServiceRouterLoader security.authentication.rememberme.services.abstract
security.authentication.rememberme.services.persistent
Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices security.authentication.rememberme.services.simplehash
Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices service_container
Symfony\Component\DependencyInjection\ContainerInterface
service_locator.1712c3a50d1ec2c742b2ead0f03bb76c
Symfony\Component\DependencyInjection\ServiceLocator
service_locator.26ac001b3ede28481ac0de703666b4d7
Symfony\Component\DependencyInjection\ServiceLocator
service_locator.39e66930232432ca5ba91e98fdd8a17b
Symfony\Component\DependencyInjection\ServiceLocator
service_locator.6f24348b77840ec12a20c22a3f985cf7
Symfony\Component\DependencyInjection\ServiceLocator
service_locator.8925f20c49cbd61fcb37adf8c595459e
Symfony\Component\DependencyInjection\ServiceLocator
service_locator.b8d2046fb854cde05549fb309e1a80d2
alias for "1_bcf140bb848ef41617942628c8525b4872574e826d00fee6aaabbf2ede89fbb8"
service_locator.ceb8bbb9f48e8bfd1c8ec2d209eabdca
Symfony\Component\DependencyInjection\ServiceLocator

If I comment out the SiteBundle\Service\CustomerService definition, the debug:container command throws this exception:

PHP Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\AutowiringFailedException: Cannot autowire service "SiteBundle\Entity\CustomerMasterRepository": argument "$em" of method "Doctrine\ORM\EntityRepository::__construct()" must have a type-hint or be given a value explicitly. Which, frankly, I can't make heads nor tails of.

Suggestions? Thanks.

Upvotes: 2

Views: 5117

Answers (2)

Tomas Votruba
Tomas Votruba

Reputation: 24280

This is the main issue:

PHP Fatal error: Uncaught Symfony\Component\DependencyInjection\Exception\AutowiringFailedException: Cannot autowire service "SiteBundle\Entity\CustomerMasterRepository": argument "$em" of method "Doctrine\ORM\EntityRepository::__construct()" must have a type-hint or be given a value explicitly. Which, frankly, I can't make heads nor tails of.

That happens, when you use EntityRepository as s service, but inherit from the Doctrine BaseRepository. This combination should never happen.

The repository should use composition, to get to Doctrine Like this:

<?php

namespace AppBundle\EntityRepository;

use AppBundle\Entity\Channel;
use Doctrine\ORM\EntityManager;

class ChannelRepository
{
    private $repository;

    public function __construct(EntityManager $entityManager)
    {
        $this->repository = entityManager->getRepository(Channel::class);
    }

    public function getOneByName(string $name)
    {
        return $this->repository->findOneBy(['name' => $name]);
    }
}

There is a post with nice code examples if you want to know more: How to use Repository with Doctrine as Service in Symfony

Upvotes: 1

Amadu Bah
Amadu Bah

Reputation: 2989

To make a custom repository work I had to override the __construct method to provide the EntityManager and the ClassMetadata as shown below:

app/config/services.yml

services:
    _defaults:
        autowire: true
        autoconfigure: true
        public: false

    app.repository.channel:
        class: \AppBundle\EntityRepository\ChannelRepository

src/AppBundle/EntityRepository/ChannelRepository.php:

<?php

namespace AppBundle\EntityRepository;

use AppBundle\Entity\Channel;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;

class ChannelRepository extends RepositoryFactory
{
    public function __construct(EntityManager $entityManager)
    {
        parent::__construct($entityManager, new ClassMetadata(Channel::class));
    }

    public function getOneByName(string $name)
    {
        return $this->findOneBy(['name' => $name]);
    }
}

src/AppBundle/Entity/Channel.php

<?php

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="channel")
 * @ORM\Entity(repositoryClass="\AppBundle\EntityRepository\ChannelRepository")
 *
 */
class Channel
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }
}

Upvotes: 0

Related Questions