Symfony Dependency injection with EntityMenager - symfony

I had to call controller in service. In that manner I had created connstruct function and add parameter EntityMenager
class UsersController extends Controller
{
private $em;
public function __construct(EntityManager $em) {
$this->em = $em;
}
and defined service for that controller
services:
user_controller:
class: AppBundle\Controller\UsersController
arguments:
['#doctrine.orm.entity_manager']
when I call that service
$usersContainer = $this->get('user_controller');
in other controllers everything is fine.
But when I call self controller I get error
Catchable Fatal Error:
Argument 1 passed to AppBundle\Controller\UsersController::__construct()
must be an instance of Doctrine\ORM\EntityManager, none given,
Where I making mistake?

I just do ugly hack and solve problem at this moment.
I had removed __construc class and add EntityManager as needed parameter to function that needs EntityManager class.
public function getLoggedUserData($loggedUserId, $entityManager)
{
$user = $entityManager->getRepository('...')
$newRequests = $entityManager->getRepository('...')

You can only define a Controller as service, if it is a simple class (not extending Controller).
See : http://symfony.com/doc/current/cookbook/controller/service.html
And particularly :
http://symfony.com/doc/current/cookbook/controller/service.html#alternatives-to-base-controller-methods

Related

Symfony 4.4 service autowired in controller's constructor doesn't get call from service definition (setContainer)

Foreword: please disregard injecting the whole container and other unclean things, I just wanted to show not-working example, this is before refactor.
I have a service defined in YAML:
app.service.my_service:
class: App\Services\MyService
public: true
calls:
- [setContainer, ['#service_container']]
part of my service:
class MyService implements ContainerAwareInterface
{
/** #var ContainerInterface */
private $container;
/** #var EntityManagerInterface */
private $em;
public function setContainer(?ContainerInterface $container = null)
{
$this->container = $container;
$this->em = $container->get('doctrine')->getManager();
}
Then I have a controller, which have that service autowired in constructor, and not instantiaded from container:
class MyController
{
/**
* #var MyService
*/
private $my_service;
function __construct(MyService $my_service) {
$this->my_service = $my_service;
}
While it actually autowires service itself, it completely ignores the setContainer call, so I end up with empty container.
I wanted to avoid calling $this->get('app.service.my_service') everywhere and I can't simply call it once in controller's contructor, or call setContainer in the constructor on autowired service, because at that time container is empty.
Any chances I can do it in a clean way?
Since you already know injecting the container is a bad idea then I guess I'll spare you the lecture. I have not encountered this particular issue but you can try using the little known '#required' container directive and see if it helps.
/** #required */
public function setContainer(ContainerInterface $container)
{
$this->container = $container;
$this->em = $container->get('doctrine.orm.default_entity_manager);
}
This is more of a guess than an answer but it is easy to try.
Update: It worked. Cool.
I just wanted to add that, in most cases, dependencies should be injected into the constructor. This avoids having partially initialized services. On the other hand, as long as you are generating your services via the container then it is not really a problem.
I find it to be very useful in traits. For example:
trait RouterTrait
{
private RouterInterface $router;
/** #required */
public function setRouter(RouterInterface $router)
{
$this->router = isset($this->router) ? $this->router: $router;
}
// These are just copied from AbstractController
protected function generateUrl(
return $this->router->generate(...
protected function redirectToRoute(
#
class SomeService
use RouterTrait;
public function someMethod()
$this->generateUrl('''
So if a service needs to do some routing then they just need to use the router trait and the router is automatically injected. Saves adding boilerplate code to the service's constructor. I do put a guard around setRouter so it can only be effectively called once which eliminates another concern about using setters for dependencies.
Update #2:
The problem with the original code is that the MyService was defined with a service is of app.service.my_service and not the class name. Autowire actually generated a second MyService and injected it since autowire looks for service ids that match the class name. Changing the service definition to:
App\Services\MyService:
public: true
calls:
- [setContainer, ['#service_container']]
Would have also worked. Or explicitly injecting app.service.my_service into the controller.

Adding doctrine entity manager to service in symfony2

Im trying to add entity manager to my service layer in Symfony. I've researched online but none of the answers seem to work for me.
I have a class with a namespace called AppBundle\Controller. My class name is MasterController. Then what exactly do I need to add into my services.yml?
Other than that I think I only need to pass EntityManager $em into the constructor as a param and then assign $em to $this->em (private)?
E.g.
class MasterController extends Controller
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
}
It's possible to define controllers as services while at the same time, extending the framework controller class. Why do this? By doing this you can take advantage of the base functionality such as the form factory, template rendering, get user stuff etc.
At the same time, you can inject any controller specific services you need. This avoids using the container as a service locator pattern. In other words, we can push services into the controller as opposed to having the controllers pull the services. The controllers don't need to care where the services come from and become more self contained.
Example:
class PersonController extends Controller
{
private $personRepository;
public function __construct($personRepository)
{
$this->personRepository = $personRepository;
}
public function editAction(Request $request, $id)
{
$person = $this->personRepository->find($id);
$form = $this->createForm(new PersonFormType(), $person);
$form->handleRequest($request);
if ($form->isValid()) {
// Note: I add flush/persist methods to my repositories
// You could inject the entity manager and flush from it
$personRepository->flush();
return $this->redirect($this->generateUrl('wherever'));
}
return $this->render('person/edit.html.twig', array(
'form' => $form->createView(),));
}
}
Make the controller a service with:
services:
person_repository:
class: AppBundle\Entity\PersonRepository
factory_service: 'doctrine.orm.default_entity_manager'
factory_method: 'getRepository'
arguments:
- 'AppBundle\Entity\Person'
person_controller:
class: AppBundle\Controller\PersonController
calls:
- [setContainer,['#service_container']]
arguments:
-'person_repository
So basically, you end up with a standard Symfony controller with all the standard functionality but you can inject controller specific services such as repositories which in turn makes your controllers easier to read and debug. You should never need to use $this->get('service') from within your controller action methods.
Contract this with the more standard method of retrieving an entity:
$personRepository = $this->getDoctrine()->getManager()->getRepository('AppBundle\Entity\Person');
As you can see, the standard method not only ties the controller directly to doctrine but also requires the controller to know the entity's class name. You can decide which method is easier to write, understand and maintain.
First of all if your MasterController extends Symfony\Bundle\FrameworkBundle\Controller class then you have already EntityManager available by using:
$this->em = $this->getDoctrine();
But if you want to inject it by yourself to your constructor (and have Controller as a service) all you need to do is have entry in your services.yml like this:
services:
your_controller_service:
class: AppBundle\Controller\MasterController
arguments: [#doctrine.orm.entity_manager]
And that's it.
EDIT:
Remember that you need to have use Doctrine\ORM\EntityManager somewhere before your class definition to avoid Catchable fatal error

Use constructor in Symfony2 controller

I'd like to use a constructor in my Symfony2 controller, but I have this error:
ContextErrorException: Catchable Fatal Error: Argument 1 passed to Acme\ApplicationBundle\Controller\CalendarActivityController::__construct() must implement interface Symfony\Component\DependencyInjection\ContainerInterface, none given, called in /Users/root/Documents/projects/Acme/Symfony/app/cache/dev/classes.php on line 2374 and defined in /Users/root/Documents/projects/Test/Symfony/src/Acme/ApplicationBundle/Controller/CalendarActivityController.php line 16
My constructor:
public function __construct(Container $container) {
$this->container = $container;
}
I must use a constructor because sometimes I'm using my controller as a service, and in this case container is not defined.
It is possible to inject services into your controller if your controller is registered as a service itself. Read more about it in the official docs: http://symfony.com/doc/current/cookbook/controller/service.html
However, let me point out, that there's not much value in injecting the Container. It's much easier to extend the Symfony\Component\DependencyInjection\ContainerAware. Symfony will inject the container for you automatically (since ContainerAware implements the ContainerAwareInterface).
Register your controller as a service if you want to inject specific services.
Use ContainerAware otherwise.
you have to call container from dependncy injection use USE
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
private $container;
public function __construct(Container $container) {
$this->container = $container;
}

Unable to inject EntityManager into Twig extension

I have a problem injecting the EntityManger into a Symfony2 twig-extension. I did the following:
I registered a new twig extension and created a class. All of it is working.
Now I wanted to get access to the database and tried to inject the doctrine EntityManager. My first step was to add the EM as parameter to the constructor:
use XYZ\BlubBundle\Utils\SessionHelper;
class SessionHelperExtension extends \Twig_Extension
{
private $em;
private $conn;
protected $sessionHelper;
public function __construct(\Doctrine\ORM\EntityManager $em, \XYZ\BlubBundle\Utils\SessionHelper $sessionHelper) {
$this->em = $em;
$this->conn = $em->getConnection();
$this->sessionHelper = $sessionHelper;
}
[...]
}
?>
Then I added the following lines to the service.yaml:
twig.extension.sessionHelper:
class: XYZ\BlubBundle\Extensions\SessionHelperExtension
arguments: [#session_helper, #doctrine.orm.entity_manager]
tags:
- { name: twig.extension }
My own Service "sessionHelper" gets injected without a problem, but the EntityManager is always "null". What am I doing wrong here?
EDIT:
I get the following exception:
Catchable Fatal Error: Argument 2 passed to
XYZ\BlubBundle\Extensions\SessionHelperExtension::__construct() must be an instance of
Doctrine\ORM\EntityManager, none given,
called in /.../Symfony/app/cache/dev/appDevDebugProjectContainer.php on line 2918 and
defined in /.../Symfony/src/XYZ/BlubBundle/Extensions/SessionHelperExtension.php line 12
public function __construct(
\Doctrine\ORM\EntityManager $em,
\XYZ\BlubBundle\Utils\SessionHelper $sessionHelper
) {
is wrong — you need to keep the same order like in the yml:
public function __construct(
\XYZ\BlubBundle\Utils\SessionHelper $sessionHelper,
\Doctrine\ORM\EntityManager $em
) {
Update
Try this: http://www.coderelic.com/2012/06/querying-the-database-from-a-twig-extension-in-symfony-2/

Service DependencyInjection in Symfony2

I needed to move my model from the controller method, so I got help to change it to a service. The service by itself works, but I need to be able to connect to doctrine and the kernel from inside of this service. At first I tried to enable doctrine, but that created problems. How can I make this work? I've followed docs and got this code. I have no idea why I got the error below. Thank you for your help in advance.
My config is:
CSVImport.php
namespace Tools\TFIBundle\Model;
use Doctrine\ORM\EntityManager;
class CSVImport {
protected $em;
public function __construct( EntityManager $em ) {
$this->em = $em;
}
app/config/config.yml
services:
csvimport:
class: Tools\TFIBundle\Model\CSVImport
arguments: [ #doctrine.orm.entity_manager ]
action in controller
$cvsimport = $this->get('csvimport');
MY ERROR
Catchable Fatal Error: Argument 1 passed to
Tools\TFIBundle\Model\CSVImport::__construct() must be an instance of
Doctrine\ORM\EntityManager, none given, called in
.../Tools/TFIBundle/Controller/DefaultController.php on line 58 and defined in
.../Tools/TFIBundle/Model/CSVImport.php line 12
EDIT, my working code:
service class code with Kernel attached to it
namespace Tools\TFIBundle\Model;
use Doctrine\ORM\EntityManager,
AppKernel;
class CSVImport {
protected $em;
protected $kernel;
protected $cacheDir;
public function __construct( EntityManager $em, AppKernel $k ) {
$this->em = $em;
$this->kernel = $k;
}
Try injecting #doctrine.orm.default_entity_manager.
On web I've found how to connect to Doctrine DBAL to be able to make queries on my own. But when i changed my configuration to this one:
app/config.yml
services:
csvimport:
class: Tools\TFIBundle\Model\CSVImport
arguments: [ #doctrine.dbal.connection, #doctrine.orm.entity_manager, #kernel ]
class definition
namespace Tools\TFIBundle\Model;
use Doctrine\ORM\EntityManager,
Doctrine\DBAL\Connection,
AppKernel;
class CSVImport {
protected $c;
protected $em;
protected $kernel;
public function __construct(Connection $c, EntityManager $em, AppKernel $k ) {
$this->c = $c;
$this->em = $em;
$this->kernel = $k;
}
i got error:
RuntimeException: The definition "csvimport" has a reference to an abstract definition "doctrine.dbal.connection". Abstract definitions cannot be the target of references.
Any ideas?

Resources