Dependency Injection in Symfony - php

I'm building Symfony 3.3 application. I have a helper in a Console folder:
abstract class AbstractHelper implements HelperInterface
{
protected $httpClient;
public function __construct(HttpInterface $httpClient)
{
$this->httpClient = $httpClient;
}
}
And I have implementation of HttpInterface named HttpGuzzle into Service folder. How could I help Symfony to figure out I want to inject HttpGuzzle into AbstractHelper constructor? I tried to add these line to services.yml but it doesn't work:
AppBundle\Command\AbstractHelper:
arguments:
$httpClient: AppBundle\Service\HttpGuzzle
If i run the tests it throws an error:
ArgumentCountError: Too few arguments to function AppBundle\Command\AbstractHelper::__construct(),
0 passed in ~/Projects/app/tests/AppBundle/Console/HelperTest.php
on line 17 and exactly 1 expected
With this:
helper:
class: AppBundle\Command\AbstractHelper:
arguments: [AppBundle\Service\HttpGuzzle]
I get an error:
You have requested a non-existent service "helper".

In services.yml You have to define the HttpGuzzle service itself, like this:
httpguzzle:
class: AppBundle\Service\HttpGuzzle
Then you can use pass it to the helper like this:
helper:
class: AppBundle\Command\AbstractHelper
arguments: ["#httpguzzle"]

Related

how to use a service inside another service in symfony 2.6

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;
}

How to setup class hierarchy with constructor injected services

I have a few logic classes implemented as services. They do some similar stuff, so I wanted to setup a hierarchy:
namespace \Company\Bundle\Service;
abstract class ParentLogic {
protected $user;
public function __construct($security_token_storage) {
$this->user = $security_token_storage->getToken()->getUser();
}
}
class ChildLogic extends ParentLogic {
}
Here is how I have the services setup
services:
parentlogic:
abstract: true
class: Company\Bundle\Service\ParentLogic
arguments: [#security.token_storage]
childlogic:
class: Company\Bundle\Service\ChildLogic
parent: parentlogic
however when I try to use the childlogic service in a controller
namespace Company\Bundle\Controller;
class TestController {
static public function getLogicService() {
return $this->get('childlogic');
}
}
I get an error saying the argument to the constructor is missing:
Warning: Missing argument 1 for Company\Bundle\Service\parentlogic::__construct(), called in /file/path/to/company/app/cache/dev/classes.php on line 2220 and defined
Is this possible? If so what am I doing wrong, or better how is it done correctly?
What Cerad said in the comment is probably true. Constructor arguments are not used on parent services. We have a similar setup with controllers where we have an AbstractController as a parent service and concrete controllers that inherit from AbstractController. However in our service definition we add any dependencies to the parent using setter-calls:
services:
abstract_controller:
class: Bundle\Controller\AbstractController
abstract: true
calls:
- [setSomething, ['#something']]
- [setAnotherThing, ['#anotherThing']]
concrete_controller:
class: Bundle\Controller\ConcreteController
parent: abstract_controller
Works like a charm.
I reckon if you wish to use constructor arguments instead you should specify them explicitly in your child service definitions as well since on creation of the child service the parent constructor will be called.

Symfony2 Use parameters in EntityRepository

I am wondering if this is even a good practice. But for my project i need to get a parameter from parameters.yml and use it inside EntityRepository.
So for this I created a service but still the call is not executed.
services:
xxx_repository:
class: XXX\DatabaseBundle\Repository\CitiesRepository
calls:
- [setTheParameter, ["%the_parameter%"]]
parameters.yml
...
the_parameter: 14400
...
And inside the CitiesRepository.php I am doing the following:
class CitiesRepository extends EntityRepository
{
/**
* #var
*/
protected $theParameter;
public function setTheParameter($theParameter)
{
$this->theParameter = $theParameter;
}
....
}
But $this->theParameter is always null.
SO i have 2 questions: Is this a healthy habit? And why is the result always null?
You need to use getRepository method of the doctrine service as factory:
xxx_repository:
class: XXX\DatabaseBundle\Repository\CitiesRepository
factory: ["#doctrine", "getRepository"]
arguments: ["DatabaseBundle:City"]
calls:
- ["setTheParameter", ["%the_parameter%"]]
And then you can access to this repository as service in your controller:
$this->get('xxx_repository');

Argument 1 passed to __construct must be an instance of Services\ProductManager, none given

in service.yml
test_product.controller:
class: MyBundle\Controller\Test\ProductController
arguments: ["#product_manager.service"]
in controller
class ProductController extends Controller
{
/**
* #var ProductManager
*/
private $productManager;
public function __construct(ProductManager $productManager){
$this->productManager = $productManager;
}
}
in routing.yml
test_product_addNew:
path: /test/product/addNew
defaults: { _controller:test_product.controller:addNewAction }
I want to use ProductManger in contructor to do some stuff but it gives me this error
Catchable Fatal Error: Argument 1 passed to
MyBundle\Controller\Test\ProductController::__construct()
must be an instance of MyBundle\Services\ProductManager,
instance of Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine given,
called in
..../app/cache/dev/appDevDebugProjectContainer.php
on line 1202 and defined
I am new to symfony, any help is appreciated
You have inverted the logical of services.
First, it's your manager wich must be defined as a service because it's it you will need to call from controller.
// services.yml
product_manager:
class: MyBundle\Path\To\ProductManager
Then call directly your manager defined as a service in your controller.
// Controller
class ProductController extends Controller
{
[...]
$this->get('product_manager');
[...]
}
And you do not need to overload __construct() methode. Only call ->get(any_service) where you need it.
Also your route is wrong. You have to define controller from is namespace.
// routing.yml
test_product_addNew:
path: /test/product/addNew
defaults: { _controller:MyBundle:Product:addNew }
Since Symfony 3.3 (released May 2017) you can use contructor injection and autowiring with ease:
# services.yml
services
_defaults:
autowire: true
MyBundle\Controller\Test\ProductController: ~
Keep rest as you already had.
Do you want to know more about there features? Check this post with examples.

Symfony2 Outside class

I use symfony2 (2.6) and I have class to global variable to twig. For Example, class menu:
namespace Cms\PageBundle\Twig;
use Doctrine\ORM\EntityManager;
class Menu {
protected $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function show(){
/******/
}
}
and services.yml
services:
class_menu:
class: Cms\PageBundle\Twig\Menu
arguments: ['#doctrine.orm.entity_manager']
twig_menu:
class: Cms\PageBundle\Twig\Menu
See:
ContextErrorException in Menu.php line 9:
Catchable Fatal Error: Argument 1 passed to
Cms\PageBundle\Twig\Menu::__construct() must be an instance of
Doctrine\ORM\EntityManager, none given, called in
/home/cms/public_html/app/cache/dev/appDevDebugProjectContainer.php on
line 3834 and defined
General, any class (outside) have problem with the constructor and (argument) doctrine.
Why?
Symfony2 getdoctrine outside of Model/Controller
This error is totally expected. Symfony2 expects to create service instance by invoking the __construct constructor. If you want to keep the single class in play, you will need to remove that __construct and use setter dependency injection instead.
There is an official documentation on this: Optional Dependencies: Setter Injection
Basically, you do not pass the EntityManager instance during the creation of an service instance but rather "set it later".
Hope this helps.
Update:
If you fallback to your original solution, make sure you pass EntityManager in both instances:
services:
class_menu:
class: Cms\PageBundle\Twig\Menu
arguments: ['#doctrine.orm.entity_manager']
twig_menu:
class: Cms\PageBundle\Twig\Menu
arguments: ['#doctrine.orm.entity_manager']

Categories