I have created an abstract BaseController class that extends AbstractController.
This is so that all the Common Dependencies don't have to be injected in each Controller class that I have (e.g. EntityManager and RequestStack).
However, I have some Controller classes where I would like to inject additional services in the constructor, but this is causing problems.
// src/Controller/BaseController.php
namespace App\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RequestStack;
abstract class BaseController extends AbstractController
{
protected $em;
protected $request;
public function __construct(EntityManagerInterface $em, RequestStack $request)
{
$this->em = $em;
$this->request = $request->getCurrentRequest();
}
}
I can then just extend my Controller classes and call for example $this->em in any of the methods.
However, let's say that I wanted to do the following:
// src/Controller/DashboardController.php
namespace App\Controller;
use Symfony\Component\Translation\TranslatorInterface;
class DashboardController extends BaseController
{
public function __construct(TranslatorInterface $translator)
{
parent::__construct();
$this->translator = $translator;
}
public function index()
{
// use $this->translator()
}
}
This would cause an error as the constructor of the BaseController is expecting two arguments to be passed.
I've tried adding the following to my services.yaml but to no avail:
App\Controller\BaseController:
arguments: ['#doctrine.orm.entity_manager', '#request_stack']
What would be the best way to autowire these arguments, and would this be a good practice?
Related
In previous versions of symfony, you could fetch objects like this
`
public function someMethod()
{
$method = $this->getDoctrine()->getRepository(Method::class)->findOneBy(array('id' => 1));
return $method;
}
`
This was easy because it meant that you could easily make global variables in the twig.yaml file and have dynamic content all around your page.
Now in symfony as far as i know, an argument of ManagerRegistry has to be passed as a argument all the time. Am I being too close minded or is there a work around for this problem?
I tried extending classes and have it pass down that way but it gave me the same workaround errors.
In a controller you can do either this :
class MyController extends AbstractController {
private EntityManagerInterface $em;
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
}
or
class MyController extends AbstractController {
{ ... }
public function someMethod(EntityManagerInterface $em) {
$em->getRepository(SomeClass::class)
}
}
I took a controller as an example but you can do this into your services. If you work with multiple entity manager, you can use the ManagerRegistry that extends AbstractManagerRegistry and use the method getManager($name)
There is no real workaround for this as you always need to inject it. Depending on what you want to achieve, may be there is solution that can help.
you can also directly inject the repository:
public function someMethod(EntityRepository $repository) {
$entities = $repository->findAll();
}
and of course you can inject it in the construct as well:
class MyController extends AbstractController {
private EntityRepository $repository;
public function __construct(EntityRepository $repository) {
$this->repository = $repository;
}
}
if you are on php 8 you can write:
class MyController extends AbstractController {
public function __construct(private EntityRepository $repository) {}
}
I have an error on a symfony 3.4 project.
I'm trying to manage the display of a notification in the menu of my application.
So I created a CustomController which extends Controller.
Then I made all my other controllers inherit from CustomController.
But when I make a call to getDoctrine() to reach a repository I get the following error:
"Call to a member function has() on null"
Here is my CustomController:
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class CustomController extends Controller
{
public $data = [];
protected $em;
public function __construct()
{
$this->em = $this->getDoctrine()->getManager();
$countAttente = $this->em->getRepository('AppBundle:Commandes')->tailleEnAttente("En attente");
$this->data['countAttente'] = $countAttente;
}
}
I tried to pass the controller as a service in service.yml but it did not change anything
AppBundle\Controller\CustomController:
class: AppBundle\Controller\CustomController
arguments: ["#doctrine.orm.entity_manager"]
calls:
- [setContainer, ["#service_container"]]
I found many similar topics on this type of error but none of them allowed me to skip this error
Any help is welcome
Autowire your EntityManager directly inside your constructor:
use Doctrine\ORM\EntityManagerInterface;
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
Or if you need a specific repository, and autowired is set up with the default configuration you can do the same as well with the repository:
private $repository;
public function __construct(CommandesRepository $repository)
{
$this->repository = $repository;
}
I have a repository class called EmailRepository
class EmailRepository extends EntityRepository implements ContainerAwareInterface { ... }
I need to get a parameter injected into this repository class but I dont know how...
This is what I currently have inside of the repository, which is being called from my controller:
Controller:
$em->getRepository(Email::class)->getEmailApi();
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface {
protected $container;
public function setContainer(ContainerInterface $container = null) {
$this->container = $container;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
echo $this->container->getParameter('email_api');
}
}
I always get this error:
Call to a member function getParameter() on null
The parameter is not null, it does have a value. I know it's telling me that $this->container is null. How do I fix this?
If I run this inside of my controller, it works fine and returns Google
echo $this->getParameter('email_api');
Inject container not a good idea. Try this
services.yaml
App\Repository\EmailRepository:
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository
{
protected $emailApi;
public function __construct(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Or via setter injection
services.yaml
App\Repository\EmailRepository:
calls:
- method: setEmailApi
arguments:
$emailApi: '%env(EMAIL_API)%'
Repository
class EmailRepository extends EntityRepository implements ContainerAwareInterface
{
protected $emailApi;
public function setEmailApi(string $emailApi)
{
$this->emailApi = $emailApi;
}
/**
* #param $array
*/
public function getEmailApi($array)
{
return $this->emailApi;
}
}
Your original code is not going to work because there is nothing calling EmailRepository::setContainer. Furthermore, using ContainerAware and injecting the full container is discouraged.
Fortunately, the Doctrine bundle has a new base repository class that the entity manager can use to pull the repository from container and allow you to inject additional dependencies as needed. Something like:
namespace App\Repository;
use App\Entity\Email;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
class EmailRepository extends ServiceEntityRepository // Different class to extend from
{
private $emailApi;
public function __construct(RegistryInterface $registry, ParameterBagInterface $parameterBag)
{
parent::__construct($registry, Email::class);
$this->emailApi = $parameterBag->get('email_api');
}
So in this case we inject all the parameters and then store the ones we need.
Even injecting the parameter bag is a bit frowned upon. Better to inject individual parameters though this takes just a bit more configuration as we need to use services.yaml to explicitly inject the needed parameters:
public function __construct(RegistryInterface $registry, string $emailApi)
{
parent::__construct($registry, Email::class);
$this->emailApi = $emailApi;
}
#services.yaml
App\Repository\EmailRepository:
$emailApi: 'email_api_value'
I am trying to inject public services like entityManager in the constructor of a service I created but I keep having this error :
Too few arguments to function App\Services\BillingInterface::__construct(), 0 passed in /var/www/.../src/Controller/TestController.php on line 144 and exactly 1 expected.
In my controllers, services are correctly injected in different methods but in the service I created it's not injected in the constructor.
I didn't change anything in the services.yaml as the documentation says autowire is automatic in Symfony 4.2
PS : I recently updated from Symfony 4.1 to 4.2 and I'm not sure but I think it worked before.
Maybe a library didn't updated correctly but I don't find any errors.
Here are the informations for the service
Service code :
#/src/Services/BillingInterface
namespace App\Services;
use Doctrine\ORM\EntityManagerInterface;
class BillingInterface {
private $em;
public function __construct(EntityManagerInterface $entityManager)
{
$this->em = $entityManager;
}
}
Controller code :
namespace App\Controller;
use App\Services\BillingInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestController extends AbstractController {
public function teest(EntityManagerInterface $entityManager)
{
$billing = new BillingInterface();
}
}
And If I instantiate BillingInterface with $entityManager parameter of Controller, it works but I would like it injected directly in the BillingInterface class constructor.
And finally, here is what is written in Symfony's documentation :
// src/Service/MessageGenerator.php
// ...
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function getHappyMessage()
{
$this->logger->info('About to find a happy message!');
// ...
}
}
Link : https://symfony.com/doc/current/service_container.html
Chapter : Injecting Services/Config into a Service
So, I don't know what's wrong with my Service.
Thank you for your answers.
Since your BillingInterface is a service - you need to use its instance that is provided by Symfony container instead of attempting to instantiate it by yourself. Your controller needs to inject this service in order to be able to use it:
namespace App\Controller;
use App\Services\BillingInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class TestController extends AbstractController
{
/**
* #var BillingInterface
*/
private $billing;
/**
* #param BillingInterface $billing
*/
public function __construct(BillingInterface $billing)
{
$this->billing = $billing;
}
public function teest(EntityManagerInterface $entityManager)
{
// Use $this->billing ...
}
}
I'm trying to find out why I'm receiving this error. I'm following along. However the only difference is that at the time of the recording it was done with Laravel 4.25 and I am now using Laravel 5.0.
Repositories and Inheritance
BindingResolutionException in Container.php line 785:
Target [App\Repositories\User\UserRepository] is not instantiable.
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Repositories\User\UserRepository;
use Illuminate\Http\Request;
class UsersController extends Controller {
private $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index() {
$users = $this->userRepository->getAll();
return $users;
}
}
<?php
namespace App\Repositories\User;
use App\Repositories\EloquentRepository;
class EloquentUserRepository extends EloquentRepository implements UserRepository
{
private $model;
function __construct(User $model) {
$this->model = $model;
}
}
<?php
namespace App\Repositories\User;
interface UserRepository {
public function getAll();
}
<?php
namespace App\Repositories;
abstract class EloquentRepository {
public function getAll() {
return $this->model->all();
}
public function getById() {
return $this->model->findOrFail($id);
}
}
You are type hinting an interface, and not the class itself. This error is occurring because Laravel cannot bind an interface because the binding must be instantiable. Abstract classes or interfaces are not valid unless Laravel knows the concrete (instantiable) class to substitute in for the abstract class / interface.
You will need to bind the EloquentUserRepository to the interface:
App::bind('UserRepository', 'EloquentUserRepository');