2
votes

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.

2
Check the author's answer here: stackoverflow.com/questions/44616346/… Might be the same issue. I have not fooled around much with autowire. Waiting for it to stabilize. But it looks like you need to change the way the file is loaded.Cerad
What Cerad said. Just move your services.yml file to the app/config folder.Jason Roman
Just an offtopic to repositories, you can register them as services as wellTomas Votruba

2 Answers

1
votes

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

0
votes

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;
    }
}