Strnm
Strnm

Reputation: 1016

Symfony 2: Creating a service from a Repository

I'm learning Symfony and I've been trying to create a service, using a repository. I've created my repositories and entities from generate:entity, so they should be fine.

So far what I got in my services.yml is:

parameters:
    mytest.entity: TestTestBundle:Brand
    mytest.class:  Test\TestBundle\Entity\Brand
    default_repository.class: Doctrine\ORM\EntityRepository

services:
     myservice:
          class: %default_repository.class%
          factory-service: doctrine.orm.default_entity_manager
          factory-method: getRepository
          arguments:
            - %mytest.entity%

But when I try to call the service, I get this error:

Catchable Fatal Error: Argument 2 passed to Doctrine\ORM\EntityRepository::__construct() must be an instance of Doctrine\ORM\Mapping\ClassMetadata, none given, called in 

Then I tried to create the service just using an entity. My services.yml would look like:

services:
     myservice:
          class: %mytest.class%
          factory-service: doctrine.orm.default_entity_manager
          factory-method: getRepository
          arguments:
            - %mytest.entity%

But for this, I get:

Error: Call to undefined method 
                Test\TestBundle\Entity\Brand::findAll

Does anybody know what am I doing wrong?

Thanks

Upvotes: 18

Views: 26471

Answers (7)

Vladimir Pak
Vladimir Pak

Reputation: 702

Symfony 3.3 and doctrine-bundle 1.8 there is a Doctrine\Bundle\DoctrineBundle\Repository\ContainerRepositoryFactory which helps to create repository as service.

Example

What we want

$rep = $kernel->getContainer()
    ->get('doctrine.orm.entity_manager')
    ->getRepository(Brand::class);

ORM description

# Brand.orm.yaml
...
repositoryClass: App\Repository\BrandRepository
...

Service description

# service.yaml

App\Repository\BrandRepository:
    arguments:
      - '@doctrine.orm.entity_manager'
      - '@=service("doctrine.orm.entity_manager").getClassMetadata("App\\Entity\\Brand")'
    tags:
        - { name: doctrine.repository_service }
    calls:
        - method: setDefaultLocale
          arguments:
              - '%kernel.default_locale%'
        - method: setRequestStack
          arguments:
              - '@request_stack'

Upvotes: 0

Francesco Casula
Francesco Casula

Reputation: 27130

DEPRECATION WARNING: No more factory_service and factory_method. This is how you should do it since Symfony 2.6 (for Symfony 3.3+ check below):

parameters:
    entity.my_entity: "AppBundle:MyEntity"

services:
    my_entity_repository:
        class: AppBundle\Repository\MyEntityRepository
        factory: ["@doctrine", getRepository]
        arguments:
            - %entity.my_entity%

The new setFactory() method was introduced in Symfony 2.6. Refer to older versions for the syntax for factories prior to 2.6.

http://symfony.com/doc/2.7/service_container/factories.html

EDIT: Looks like they keep changing this, so since Symfony 3.3 there's a new syntax:

# app/config/services.yml
services:
    # ...

    AppBundle\Email\NewsletterManager:
        # call the static method
        factory: ['AppBundle\Email\NewsletterManagerStaticFactory', createNewsletterManager]

Check it out: http://symfony.com/doc/3.3/service_container/factories.html

Upvotes: 44

Tomas Votruba
Tomas Votruba

Reputation: 24280

Since 2017 and Symfony 3.3+ this is now much easier.

Note: Try to avoid generic commands like generate:entity. They are desined for begginers to make project work fast. They tend to bare bad practises and take very long time to change.

Check my post How to use Repository with Doctrine as Service in Symfony for more general description.

To your code:

1. Update your config registration to use PSR-4 based autoregistration

# app/config/services.yml
services:
    _defaults:
        autowire: true

    Test\TestBundle\:
        resource: ../../src/Test/TestBundle

2. Composition over Inheritance - Create own repository without direct dependency on Doctrine

<?php

namespace Test\TestBundle\Repository;

use Doctrine\ORM\EntityManagerInterface;

class BrandRepository
{
    private $repository;

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

    public function findAll()
    {
        return $this->repository->findAll();
    }
}

3. Use in any Service or Controller via constructor injection

use Test\TestBundle\Repository\BrandRepository;

class MyController
{
    /**
     * @var BrandRepository
     */
    private $brandRepository;

    public function __construct(BrandRepository $brandRepository)
    {
        $this->brandRepository = $brandRepository;
    }

    public function someAction()
    {
        $allBrands = $this->brandRepository->findAll();
        // ...
    }

}

Upvotes: 2

user3665895
user3665895

Reputation: 19

sf 2.6+

parameters:
    mytest.entity: TestTestBundle:Brand
    mytest.class:  Test\TestBundle\Entity\Brand
    default_repository.class: Doctrine\ORM\EntityRepository

services:
     myservice:
          class: %default_repository.class%
          factory: ["@doctrine.orm.default_entity_manager", "getRepository"]
          arguments:
            - %mytest.entity%

Upvotes: -2

Rivsen
Rivsen

Reputation: 431

I convert service.yml to service.xml, and update DependencyInjection Extension, everything is working for me. I don't know why, but yml config will thrown Catchable Fatal Error. You can try using xml config for service config.

service.yml:

services:
    acme.demo.apikey_userprovider:
        class: Acme\DemoBundle\Entity\UserinfoRepository
        factory-service: doctrine.orm.entity_manager
        factory-method: getRepository
        arguments: [ AcmeDemoBundle:Userinfo ]

    acme.demo.apikey_authenticator:
        class: Acme\DemoBundle\Security\ApiKeyAuthenticator
        arguments: [ "@acme.demo.apikey_userprovider" ]

service.xml:

<services>
    <service id="acme.demo.apikey_userprovider" class="Acme\DemoBundle\Entity\UserinfoRepository"  factory-service="doctrine.orm.entity_manager" factory-method="getRepository">
        <argument>AcmeDemoBundle:Userinfo</argument>
    </service>

    <service id="acme.demo.apikey_authenticator" class="Acme\DemoBundle\Security\ApiKeyAuthenticator">
        <argument type="service" id="acme.demo.apikey_userprovider" />
    </service>
</services>

Upvotes: 0

Florian Klein
Florian Klein

Reputation: 8915

Here is how we did it in KnpRadBundle: https://github.com/KnpLabs/KnpRadBundle/blob/develop/DependencyInjection/Definition/DoctrineRepositoryFactory.php#L9

Finally it should be:

my_service:
    class: Doctrine\Common\Persistence\ObjectRepository
    factory_service: doctrine # this is an instance of Registry
    factory_method: getRepository
    arguments: [ %mytest.entity% ]

UPDATE

Since 2.4, doctrine allows to override the default repositor factory.

Here is a possible way to implement it in symfony: https://gist.github.com/docteurklein/9778800

Upvotes: 24

Daniel
Daniel

Reputation: 109

You may have used the wrong YAML-Keys. Your first configuration works fine for me using

  • factory_service instead of factory-service
  • factory_method instead of factory-method

Upvotes: 10

Related Questions