Symfony 3: can't overwrite repository - php

I upgraded from Symfony 2.7 to 3.0. It works almost. Only problem: I'm not able to overwrite AppBundle\Entity\MyRepository in the code below, the file is not even necessary (I renamed it for testing) and got skipped by Symfony.
app/config/services.yml
# old, 2.7 (worked):
myRepositoryService:
class: AppBundle\Entity\MyRepository
factory_service: doctrine.orm.entity_manager
factory_method: getRepository
arguments: [AppBundle\Entity\MyEntity]
# new, 3.0 (skips the MyRepository):
myRepositoryService:
class: AppBundle\Entity\MyRepository
factory: ['#doctrine.orm.entity_manager', 'getRepository']
arguments: ['AppBundle\Entity\MyEntity']
AppBundle\Entity\MyRepository
class MyRepository extends EntityRepository {
public function findAll() {
die("MyRepository->findAll()"); // not executed
}
}
AppBundle\Controller\MyController.php
$myItems = $this->get('myRepositoryService')->findAll(); // should die but doesn't
Did I misconfigure my services.yml so that Symfony creates a temporary repository file for the entity instead of using the file I created?
Thanks in advance!

Following code is working for me in Symfony 3.0:
some_repository:
class: AppBundle\Repository\SomeRepository
factory: ['#doctrine.orm.default_entity_manager', getRepository]
arguments: [AppBundle:SomeEntity]
Please note the different than yours factory service name and that there are no single quotes for factory method and entity name.
Also as I've mentions in my comment, there should be repositoryClass set in entity's mapping.

Related

Symfony 3.4 - Auto wire of service not working in method

I'm relatively new to Symfony, and I'm having trouble some trouble.
I'm trying to type hint a custom RequestValidator class in the method being called when the endpoint is called.
Using Symfony 3.4
However, I am getting the following error:
Controller "ApiBundle\Endpoints\Healthcheck\v1\Index::check()" requires that you provide a value for the "$request" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
Here is my setup:
services.yml file
...
_defaults:
autowire: true
autoconfigure: true
...
routing.yml
api.Healthcheck:
path: /healthcheck
controller: ApiBundle\Endpoints\Healthcheck\v1\Index::check
defaults: { _format: json }
methods:
- GET
And then - inside the Index class, I have the following:
<?php
namespace ApiBundle\Endpoints\Healthcheck\v1;
use ApiBundle\Responses\ApiResponse;
class Index extends ApiResponse
{
public function check(HealthcheckRequest $request) {
var_dump($request);die;
}
}
When I do debug:autowiring I see my HealthcheckRequest in the list.
Further, when I do the same and try type-hint in the constructor of the Index class, it all works.
And finally, if I try and type hint the Symfony/HttpFoundation/Request, inside the check() method, it instantiates it correctly.
In summary:
Not working :
check(HealthcheckRequest $request)
Working:
__construct(HealtcheckRequest $request)
check(SymfonyRequest $request)
Am I doing something wrong? Any help is appreciated.
It's part of services.yaml already in Symfony 4, but introduced in version 3.3, so this might help:
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
ApiBundle\Endpoints\:
resource: '../../Endpoints/*'
tags: ['controller.service_arguments']

Symfony 4 - how to use service tags when autowiring an entire path

I'm working on a bundle for Symfony 4 that is structured like this:
\Acme
\FooBundle
\Article
\Entity
- Article.php
- Comment.php
\Form
- ArticleType.php
\Repository
- ArticleRepository.php
- CommentRepository.php
- ArticleManager.php
\User
\Entity
- User.php
\Repository
- UserRepository.php
- UserManager.php
\SomethingElse
\Entity
- SomethingElse.php
\Repository
- SomethingElseRepository.php
- SomethingElseManager.php
There are many more folders and entities, but is irrelevant for the question.
Autowiring all the classes in that folder can be created with a config like this one:
Acme\FooBundle\:
resource: '../../*/{*Manager.php,Repository/*Repository.php}'
exclude: '../../{Manager/BaseManager.php,Repository/BaseRepository.php}'
autowire: true
But when you need to add service tags like doctrine.repository_service, this kind of configuration won't help no more. Without the tag, when using in controller like:
$this->getDoctrine()->getRepository(Bar::class)
or
$this->getDoctrine()->getManager()->getRepository(Bar::class)
it throws an error:
The "Acme\FooBundle\SomethingElse\Repository\SomethingElseRepository" entity repository implements "Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface", but its service could not be found. Make sure the service exists and is tagged with "doctrine.repository_service".
The thing is that, since they all reside in the same root folder I'm not allowed to use a config like the following one, because it would have duplicated Acme\FooBundle\ keys:
Acme\FooBundle\:
resource: '../../*/{*Manager.php}'
exclude: '../../{Manager/BaseManager.php}'
autowire: true
Acme\FooBundle\:
resource: '../../*/{Repository/*Repository.php}'
exclude: '../../{Repository/BaseRepository.php}'
autowire: true
tags: ['doctrine.repository_service']
So, I was wondering if there's a workaround that I couldn't find or I should just manually add each and every service?
Edit:
It would have been a nice feature to be able to use an annotation in the class so when it's loaded it "knows" it's tag, but I'm thinking it works the other way around, loading a class because is was tagged with a certain tag.
I had the same error message after upgrading to symfony 4.4 from 3.4.
The issue seemed to be that the entity had an annotation to #ORM\Entity(repositoryClass="App\Repository\MyRepository")
while the repository extends ServiceEntityRepository and in the constructor points to the entity parent::__construct($registry, MyEntity::class);.
Removing the annotation on the entity fixed the issue.
I encountered the same error message after refactoring (renaming) some entities and the related repositories using PhpStorm 2019.2 The refactor did not update the repository class name in the doc block for the entity:
* #ORM\Entity(repositoryClass="App\Repository\OldRepository")
So I used right-click > Copy Reference to get the fully qualified name of NewRepository and pasted it in to the doc block reference:
* #ORM\Entity(repositoryClass="\App\Repository\NewRepository")
PhpStorm prefixed the class with a backslash and I didn't notice until after trying many combinations of suggested solutions for this error. I only needed to remove the backslash and the error is gone:
* #ORM\Entity(repositoryClass="App\Repository\NewRepository")
You can autoconfigure tags in your Kernel / Main Bundle Class:
https://symfony.com/doc/current/service_container/tags.html#autoconfiguring-tags
<?php
namespace Acme\FooBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class FooBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->registerForAutoconfiguration(EntityRepository::class)
->addTag('doctrine.repository_service');
}
}
You can tag all of your repositories, like this:
App\Repository\:
resource: '../src/Repository'
autowire: true
tags: ['doctrine.repository_service']
Thanks #t-van-den-berg and #arleigh-hix !
I had this problem after migrating from Symfony 3.4 to 4.4, when I wanted to use old Repositories with new services.
My solution was a little variation:
use App\Repository\NewRepository;
//...
/**
* #ORM\Entity(repositoryClass=NewRepository::class)
*/
And service declaration (to use Interface):
App\Repository\NewRepository:
arguments:
- "#doctrine"
App\Repository\NewRepositoryInterface: '#App\Repository\NewRepository'

Symfony3 controller constructor injection is not working

I want to pass the EntityManager instance into the constructor of my controller, using this code:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Doctrine\ORM\EntityManager;
class UserController extends Controller
{
public function __construct( EntityManager $entityManager )
{
// do some stuff with the entityManager
}
}
I do the constructor injection by putting the parameters into the service.yml file:
parameters:
# parameter_name: value
services:
# service_name:
# class: AppBundle\Directory\ClassName
# arguments: ["#another_service_name", "plain_value", "%parameter_name%"]
app.user_controller:
class: AppBundle\Controller\UserController
arguments: ['#doctrine.orm.entity_manager']
the service.yml is included in the config.yml and when I run
php bin/console debug:container app.user_controller
I get:
Information for Service "app.user_controller"
=============================================
------------------ -------------------------------------
Option Value
------------------ -------------------------------------
Service ID app.user_controller
Class AppBundle\Controller\UserController
Tags -
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autowiring Types -
------------------ -------------------------------------
However, calling a route which is mapped to my controller, I get:
FatalThrowableError in UserController.php line 17: Type error:
Argument 1 passed to
AppBundle\Controller\UserController::__construct() must be an instance
of Doctrine\ORM\EntityManager, none given, called in
/home/michel/Documents/Terminfinder/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
on line 202
I cant figure out, why the EntityManager is not getting injected?
When using the base classController.php the Container is usually auto-wired by the framework in theControllerResolver.
Basically you are trying to mix up how things actually work.
To solve your problem you basically have two solutions:
Do no try to inject the dependency but fetch it directly from the Container from within your action/method.
public function listUsers(Request $request)
{
$em = $this->container->get('doctrine.orm.entity_manager');
}
Create a controller manually but not extend the Controller base class; and set ip up as a service
To go a bit further on this point, some people will advise to do not use the default Controller provided by Symfony.
While I totally understand their point of view, I'm slightly more moderated on the subject.
The idea behind injecting only the required dependencies is to avoid and force people to have thin controller, which is a good thing.
However, with a little of auto-determination, using the existing shortcut is much simpler.
A Controller / Action is nothing more but the glue between your Views and your Domain/Models.
Prevent yourself from doing too much in your Controller using the ContainerAware facility.
A Controller can thrown away without generate business changes in your system.
Since 2017 and Symfony 3.3+, there is native support for controllers as services.
You can keep your controller the way it is, since you're using constructor injection correctly.
Just modify your services.yml:
# app/config/services.yml
services:
_defaults:
autowire: true
AppBundle\:
resouces: ../../src/AppBundle
It will:
load all controllers and repositories as services
autowire contructor dependencies (in your case EntityManager)
Step further: repositories as services
Ther were many question on SO regarding Doctrine + repository + service + controller, so I've put down one general answer to a post. Definitelly check if you prefer constructor injection and services over static and service locators.
Did you use following pattern to call the controller AppBundle:Default:index? if yes that should be the problem. If you want to use controller as a service you have to use the pattern: app.controller_id:indexAction which uses the id of the service to load the controller.
Otherwise it will try to create an instance of the class without using the service container.
For more information see the symfony documentation about this topic https://symfony.com/doc/current/controller/service.html
The entity manager is available in a controller without needing to inject it. All it takes is:
$em = $this->getDoctrine()->getManager();

Override translator class not working in symfony3

I need to know how to override the trans method in symfony3.
In symfony2 project we used to override the parameter for the translator class
parameters:
translator.class: Acme\HelloBundle\Translation\Translator
We started using symfony3 and try to apply the same approach but unfortunately the parameter was removed and the class path is written directly into the service.
You can override an existing definition (e.g. when applying the Decorator pattern). This kind of decoration is supported by the Dependency Injection Container of Symfony as described in the doc. As example:
services:
app.mailer:
class: AppBundle\Mailer
# this replaces the old app.mailer definition with the new one, the
# old definition is lost
app.mailer:
class: AppBundle\DecoratingMailer
and:
services:
# ...
app.decorating_mailer:
class: AppBundle\DecoratingMailer
decorates: app.mailer
arguments: ['#app.decorating_mailer.inner']
public: false
Hope this help

Overriding Default FOSUserBundle Forms: InvalidArgumentException

I'm using official documentation for symfony 2.1 for overriding default FOSUserBundle forms. But when I tried to override form handlers I've got this error:
InvalidArgumentException: The service definition
"test_user.form.handler.registration" does not exist.
Files listing:
<?php
// src/Test/UserBundle/Form/Handler/RegistrationFormHandler.php
namespace Test\UserBundle\Form\Handler;
use FOS\UserBundle\Form\Handler\RegistrationFormHandler as BaseHandler;
use FOS\UserBundle\Model\UserInterface;
class RegistrationFormHandler extends BaseHandler
{
protected function onSuccess(UserInterface $user, $confirmation)
{
// logic here...
parent::onSuccess($user, $confirmation);
}
}
=-=-=-=-=
# src/Test/UserBundle/Resources/config/services.yml
services:
test_user.form.handler.registration:
class: Test\UserBundle\Form\Handler\RegistrationFormHandler
arguments: ["#fos_user.registration.form", "#request", "#fos_user.user_manager", "#fos_user.mailer", "#fos_user.util.token_generator"]
scope: request
public: false
=-=-=-=-=
# app/config/config.yml
fos_user:
db_driver: orm
firewall_name: dev
user_class: Test\UserBundle\Entity\User
registration:
form:
handler: test_user.form.handler.registration
P.S. Yes, I have read similar questions, but there is not the same problem that I have.
Take a close look at the last line in services.yml. It says public: false, that is, this service is not accessible from the code (see Advanced Container Configuration). You can do one of two things: change the value of public to true or remove the line since the default value is true.
The problem was simple. I created my UnserBundle manually, so folder named DependencyInjection has not been created. This folder contains php files, which load services configuration.

Categories