In a Symfony2 project, when you use a Controller, you can access Doctrine by calling getDoctrine() on this, i.e.:
$this->getDoctrine();
In this way, I can access the repository of such a Doctrine Entity.
Suppose to have a generic PHP class in a Symfony2 project. How can I retrieve Doctrine ?
I suppose that there is such a service to get it, but I don't know which one.
You can register this class as a service and inject whatever other services into it. Suppose you have GenericClass.php as follows:
class GenericClass
{
public function __construct()
{
// some cool stuff
}
}
You can register it as service (in your bundle's Resources/config/service.yml|xml usually) and inject Doctrine's entity manager into it:
services:
my_mailer:
class: Path/To/GenericClass
arguments: [doctrine.orm.entity_manager]
And it'll try to inject entity manager to (by default) constructor of GenericClass. So you just have to add argument for it:
public function __construct($entityManager)
{
// do something awesome with entity manager
}
If you are not sure what services are available in your application's DI container, you can find out by using command line tool: php app/console container:debug and it'll list all available services along with their aliases and classes.
After checking the symfony2 docs i figured out how to pass your service
in a custom method to break the default behavior.
Rewrite your configs like this:
services:
my_mailer:
class: Path/To/GenericClass
calls:
- [anotherMethodName, [doctrine.orm.entity_manager]]
So, the Service is now available in your other method.
public function anotherMethodName($entityManager)
{
// your magic
}
The Answer from Ondrej is absolutely correct, I just wanted to add this piece of the puzzle to this thread.
Related
I am developing a Symfony 3 application. Symfony profiler logs tell me:
Relying on service auto-registration for type "App\Entity\SubDir\Category"
is deprecated since version 3.4 and won't be supported in 4.0.
Create a service named "App\Entity\SubDir\Category" instead.
Yet, this is a simple ORM bean:
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
...
How should I get rid of this issue? Do I really need to declare ORM entities as services in services.yaml? If yes, how?
Update
In fact, my entity is in a sub directory. I have amended my question.
In my service.yaml, I have tried:
App\:
resource: '../src/*'
exclude: '../src/{Entity,Repository,Tests,Entity/SubDir}'
...but to no avail.
Do you have any Classes under Service-auto registration which use an Entity as constructor argument?
That's where your problem comes from.
You need to ask yourself if the concerning class really is a service or just a plain object of which you always create the instance yourself.
If it is not used as a service through the container you have 2 options:
You can exclude this class also through the glob pattern like for example
AppBundle\:
resource: '...'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../{Entity,PathToYourNotService}'
or you can set the following parameter in your config
parameters:
container.autowiring.strict_mode: true
with this option the container won't try to create a service class with arguments that are not available as services and you will get a decisive error. This is the default setting for sf4
A good example for a class that triggers exactly this error would be a custom event class that takes an entity as payload in the constructor:
namespace AppBundle\Event;
use AppBundle\Entity\Item;
use Symfony\Component\EventDispatcher\Event;
class ItemUpdateEvent extends Event
{
const NAME = 'item.update';
protected $item;
public function __construct(Item $item)
{
$this->item = $item;
}
public function getItem()
{
return $this->item;
}
}
Now if this file isn't excluded specifically the container will try to auto register it as service. And because the Entity is excluded it can't autowire it. But in 3.4 there's this fallback which triggers this warning.
Once the strict_mode is activated the event just won't be available as service and if you tried using it as one an error would rise.
I have a service setup in symfony 2.6 by name ge_lib and it looks like below
ge_lib:
class: GE\RestBundle\Services\GELib
arguments: [#session, #doctrine.orm.entity_manager, #manage_ge_proc]
inside GELib.php I have a requirement to use a function from another service manage_ge_proc
manage_ge_proc:
class: GE\RestBundle\Services\GEManageProcedure
arguments: [#doctrine.orm.entity_manager, #manage_ge_native_query]
if I try to use it this way, it is not working
$emailInv = $this->get('manage_ge_proc');
$sendStatus = $emailInv->pSendGeneralEmail(.....);
It gives error saying that unable to find any get function by that name. generally this -> $this->get('manage_ge_proc');works in any controller.But how do i use it in service?.
I tried $this->getContainer()->get('manage_ge_proc'); but it did not work.
This call is fetching service from DI container, which you dont have in your service
$this->get('manage_ge_proc');
It works in controller because DI container is automatically injected there.
Since you have this line in you services.yml, which tells Symfony to inject #manage_de_proc service into ge_lib constructor
arguments: [#session, #doctrine.orm.entity_manager, #manage_ge_proc]
you should be able to pick #manage_ge_proc from constructor like this:
public function __construct(
Session $session,
EntityManager $entityManager,
GEManageProcedure $manageGeProc
)
{
//... whatever you do in your constructor
$this->manageGeProc = $manageGeProc;
}
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();
i searh and read a lot of same question but ever i got the same error :/
I create a service:
parameters:
tbackend.report.class: T\BackendBundle\Entity\Report
services:
tbackend.entity.report:
class: %tbackend.report.class%
arguments: ["%kernel.root_dir%"]
And i has this in T\BackendBundle\Entity\Report:
public function __construct($rootDir){
$this->rootDir = $rootDir;
}
When i try to create new Report(); i receive this msg:
Warning: Missing argument 1 for T\BackendBundle\Entity\Report::__construct(), called in /var/www/test/t_study/src/T/BackendBundle/Entity/ReportRepository.php on line 62 and defined
Considerations: i know the services.yml is called, i has more services in this file and all work ok (Loginhandlers, etc), i only add one more (tbackend.entity.report)
What is wrong with that? :( I dont know if need more for know about the problem. I follow symfony2 service container guide
http://symfony.com/doc/master/book/service_container.html
Basically I try not to use DIR in the Entity when moving files
Ty
When instantiating a class, you use normal PHP. Symfony isn't some magic that hooks into the instantiating process of PHP to automatically inject things in the constructor.
If you want to get a service, you either have to inject the service in the class you need it or you have the containe rin the class (for instance, in the controller) and retrieve the service from the container.
$report = $this->container->get('tbackend.entity.report');
or: (which is a much better practice in all cases except from controllers)
class WhereINeedTheReport
{
private $report;
public function __construct(Report $report)
{
$this->report = $report;
}
}
services:
# ...
where_i_need_the_report:
class: ...
arguments: ["#tbackend.entity.report"]
New to symfony and php. I was able to successfully define a service and inject the doctrine entity manager into it. It works fine but during the initialization I have to pass a string that contains the service name as follows:
$eRep = $this->container->get('employee_repository');
Can this be avoided? Can this be converted to something more elegant like
$eRep = $this->container->getEmployeeRepository();
The service is defined as:
services:
employee_repository:
class: AppBundle\Repository\EmployeeRepository
arguments: [#doctrine.orm.entity_manager]
Apologies for the noob question.
EDIT
Can I get access to a service container inside another class say EmployeeEnvelope and call as follows:
class EmployeeEnvelope{
public function getEmployeeRepository()
{
return $this->container->get('employee_repository');
}
}
If you're requesting the service from a controller, you can setup your controller to be a service too. Then you can pass the employee repository service to it using dependency injection.
That way you will not have the string reference in the controller, but in the configuration.
http://symfony.com/doc/current/cookbook/controller/service.html