I am using monolog
in class DefaultController extends Controller
such as
$logger = $this->get('logger');
$logger->info('Get Started');
I can call this->get('logger') from the class which inherits Controller class.
However I want to use logger from other class such as /Entity/User.php
How can I make it?
my reference is
http://symfony.com/doc/2.0/cookbook/logging/monolog.html
In general you can access services like the logger in classes where the container is not automatically injected ( i.e. Controller and Commands extending ContainerAwareCommand ) by using Dependency Injection.
Possible injection-types are property-,setter- and constructor injection. My example will cover constructor injection. you will first need to create a service for your class.
Assuming yml-configuration an example could look like this:
services:
your_service:
class: Vendor/YourBundle/NonControllerExtendingClass
arguments: ["#logger"] # inject logger service into constructor
In my example the 'logger' service is automatically injected in the NonControllerExtendingClass if it is called as a service. Make sure you have something like this in your Vendor/YourBundle/NonControllerExtendingClass:
use Symfony\Component\HttpKernel\Log\LoggerInterface;
// ...
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
now use the logger in your method like this:
public function someAction()
{
$this->logger->info('Im here');
}
The logger will only be injected if you call your class as a Service or inject the logger manually.
// ... gets service from the container
$my_service = $this-container->get('your_service');
// ... manual injection
$logger = $this->container->get('logger');
// alternatively create a logger object yourself i.e. $logger = new Logger();
$my_service = new Vendor/YourBundle/NonControllerExtendingClass($logger);
If you want to track entity changes with the logger you should use a Doctrine Event Listener or Subscriber.
Readmore about it in the documentation chapter - How to Register Event Listeners and Subscribers.
Because of separation of concerns, entities should not have dependencies on services. Depending on your needs, it might be wise to use the logger from the controller/service or whatever that calls the entities method that you want to log.
Generally speaking, you can define classes as services and inject the logger into that service. If you are not yet familiar with the service container and dependency injection, I would strongly advise you to read this chapter of the docs.
It might be a tricky subject to grasp. However, as it is such a vital component of symfony, it will be really worth your while to try to understand this.
Related
Using Symfony 3.4 we call services like so: $container->get('service.name')
so we make sure to give names to our services.
in Symfony 4 we inject services by class name or interface directly in the controller like so:
public function someAction(HttpClientInterface $service){
// do something here
}
so what confuses me here is that we are injecting an interface and symfony takes care of invoking the right object in the controller.
my question is: if I have 2 services that implements the same interface :
class ClassA implements InterfaceX{}
class ClassB implements InterfaceX{}
and in the controller I do this:
public function someAction(InterfaceX $service){
// do something here
}
which service gets invoked ?
Yes, services can implement the same interface and in your controller you will need to inject correct service, like this:
public function someAction(ClassA $serviceA){
// do something here
}
and
public function someOtherAction(ClassB $serviceB){
// do something here
}
If you need same service in multiple methods on the same class, use Dependency Injection on the __construct
I have a file with employees and I want to import it and make a lot of validations.
I have a service which receives each loaded employee data, does a lot of validations, create or edit the employee based on his name, and persists the entity.
that do stuff with an employee and, after that, create or edit an employee entity and persists it.
class EmployeeModel()
{
public function __contruct(string $employeeName);
public function setEntityManager(EntityManager $em);
}
I don't want to use the container, I want to go with dependency injection.
If I make a
new EmployeeModel("John Doe");
I'll have no entityManager in the $this->em because the setter wasn't called.
I could make a factory, but I don't know how to pass a runtime variable like $employeeName.
All examples I found for parameters in factories work with defined parameters, like in parameters.yml.
model.employee:
class: AppBundle\EmployeeModel
factory_service: factory.employee
factory_method: get
arguments:
- "%this_exists_in_parameter_yml%"
So, what should be the best way to do this?
Basically I need to instantiate a class and, yet, have access to services but I don't want to use the container directly. I would like to go with dependency injection.
Btw, use forms validators won't work since I am not using forms.
Never used it in an entity but can't you call EntityManager in the construct of your entity :
class EmployeeModel()
{
private $em;
public function __contruct(EntityManager $entityManager, string $employeeName) {
$this->em = $em;
}
}
i try to inject Container in my RepositoryClass, but it does not work.
BaseRepository:
<?php
namespace MyApp\ApplicationBundle\Repository;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class BaseRepository implements ContainerAwareInterface
{
protected $container;
public function setContainer(ContainerInterface $container=null)
{
echo "container";
var_dump($container);
}
public function __construct()
{
echo __CLASS__;
}
}
services.yml
services:
myapp.base_repository:
class: MyApp\ApplicationBundle\Repository\BaseRepository
calls:
- [ setContainer, [ '#service_container' ] ]
DefaultController:
$baseRep = new BaseRepository();
The only output that i get, is the echo FILE from the BaseRepository Construct.
The second way that i tried, is to inject the GuzzleClient self (this is the reason why i tried to inject the container, because i need my guzzle-configuraton-settings.
services.yml
myapp.base_repository:
class: MyApp\ApplicationBundle\Repository\BaseRepository
arguments: ['#csa_guzzle.client.mce']
BaseRepository:
use GuzzleHttp\Client;
class BaseRepository
{
public function __construct(Client $client)
{
var_dump($client);
echo __CLASS__;
}
}
But then i got the following error:
Type error: Argument 1 passed to
MyApp\ApplicationBundle\Repository\BaseRepository::__construct() must
be an instance of GuzzleHttp\Client, none given, called in
MyApp/src/Chameleon/DefaultBundle/Controller/DefaultController.php on
line 20
Anyone know what i can do?
Thank you!
To get the class that is managed by the Service Container you have to use said container to get the service with that id myapp.base_repository as Twifty says:
$this->get('myapp.base_repository');
// or more generally in classes implementing ContainerAwareInterface:
$this->container->get('myapp.base_repository');
If you create a new instance yourself you will have to manage all dependencies:
// In your controller extending Symfony's Controller:
$repository = new BaseRepository();
$repository->setContainer($this->container);
Similarly if you inject a Guzzle-client into the repository you have to either retrieve the service from the container or create it yourself with all the dependencies:
// $this->get() assumes you are in the controller as well
$repositoryWithClientFromServiceContainer = new BaseRepository(
$this->get('csa_guzzle.client.mce')
);
// This obviously works everywhere
$repositoryWithNewDefaultClient = new BaseRepository(
new GuzzleHttp\Client()
);
Furthermore injecting the service container into a class violates the dependency inversion you try to achieve by using the Service Container in the first place. This means, instead of making your repository ContainerAware you should only add the services you need in that repository, not the whole container. Just as you do in the 2nd example with the Guzzle-client.
Some people argue it's okay for controllers to violate that principle, but I personally prefer controller's being defined as services to be able to quickly see which dependencies they have by looking at the constructor.
As a general rule I would avoid using the ContainerAwareInterface.
Similarly if you inject a Guzzle-client into the repository you have
to either retrieve the service from the container or create it
yourself with all the dependencies:
// $this->get() assumes you are in the controller as well
$repositoryWithClientFromServiceContainer = new BaseRepository(
$this->get('csa_guzzle.client.mce')
);
// This obviously works everywhere
$repositoryWithNewDefaultClient = new BaseRepository(
new GuzzleHttp\Client()
);
Furthermore injecting the service container into a class violates the
dependency inversion you try to achieve by using the Service Container
in the first place. This means, instead of making your repository
ContainerAware you should only add the services you need in that
repository, not the whole container. Just as you do in the 2nd example
with the Guzzle-client.
Some people argue it's okay for controllers to violate that principle,
but I personally prefer [controller's being defined as services][1] to
be able to quickly see which dependencies they have by looking at the
constructor.
As a general rule I would avoid using the ContainerAwareInterface.
[1]: http://symfony.com/doc/current/cookbook/controller/service.html
Thank you.
So, it would be the better solution, if i inject only the guzzleClient, right?
As you can see, i have a few classes that extends from my BaseRepository and they need the guzzleClient.
But how is it possible to inject the guzzleClient for this scenario? If the programmer only want to create his basic "MyRep" Repositoryclass in the controller without any params.
services.yml
myapp.base_repository:
class: MyApp\ApplicationBundle\Repository\BaseRepository
arguments: ['#csa_guzzle.client.mce']
BaseRepository:
use GuzzleHttp\Client;
class BaseRepository
{
private $client = null;
public function __construct(Client $client)
{
var_dump($client);
$this->client = $client;
}
public getClient() {
return $this->client;
}
}
MyRepository:
MyRep extends BaseRepository:
use GuzzleHttp\Client;
class BaseRepository
{
public function __construct()
{
var_dump($this->getClient());
}
}
Thank you!
If I have a controller which is with the name space
Src/ApiMap/ApiMapBundle/controller/Getapicontroller
class GetApiController extends Controller {
fitch data on the bases of entities define in entites classes and then save that data in in table
and save it in database
}
Now I define this as service in
App/config/service.yml
services:
app.get_controller:
class: ApiMaps\ApiMapBundle\Controller\GetApiController
3rd I define a service class on the location
app/container/getApi.php
Now I want to access this controller-service in this class I means in this service how can i access because if i use
this->get function
in simple class what would i need for it.
the point is i can not move my logic to service bcz it is using entities and other stuff in controller i want to move this logic to some 3rd class service...
it location is right now
src/container
getapi.php
and its definition is right now...
<?php
namespace dino_container\GetApi;
class GetApi {
public function __construct() {
i want to incorport this controller service in this class...
}
Seems like we had this conversation before.
It makes no sense to try calling a controller method from a command. A controller method's job is to convert a Request into a Response. Commands have no request object nor would they know what to do with a Response object.
Instead you want to share functionality between a controller and a command by extracting the functionality into it's own service. You then access that service from both the command and the controller.
# services.yml
my_service:
class: MyService
arguments: ['#doctrine.orm.default_entity_manager']
# Controller
public function myAction(Request $request)
{
$myService = $this->container->get('my_service');
$myService->doSomethingShared();
# Command
public function execute()
{
$myService = $this->container->get('my_service');
$myService->doSomethingShared();
I am trying to access a service in a Symfony Controller
$session = $this->get('session');
But I get the next error:
PHP Fatal error: Call to a member function get() on a non-object
I thought that Symfony2 had the controllers defined as services by default.
Note: this question was originally asked by Dbugger, but he removed it for no reason, while it was already answered.
Using the container in controllers
get() is only a shortcut function provided by the Symfony base Controller class to access the container.
Your controller must extend this class to use this function:
namespace Acme\ExampleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
// your actions
}
If you don't want to depend on this class (for some reasons) you can extend ContainerAware to get the container injected and use it like in the get() shortcut:
namespace Acme\ExampleBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
class DefaultController extends ContainerAware
{
public function exampleAction()
{
$myService = $this->container->get('my_service');
// do something
}
}
Creating controllers on your own
Controllers are not defined as services per default, you can define them, but it's not needed to get the container. If a request is made, the routing framework determines the controller, which need to be called. Then the controller gets constructed and the container is injected via the setContainer() method.
But if you construct the controller on your own (in a test or anywhere else), you have to inject the container on your own.
$controller = new DefaultController();
$controller->setContainer($container);
// $container comes trough DI or anything else.