In the definition of my services, I'd like to pass as a service argument constructor an object not a service.
From config.yml:
services:
acme.services.exampleservice:
class: Acme\ExampleBundle\Services\ExampleService
arguments:
entityManager: "#doctrine.orm.entity_manager"
httpClient: \Example\Http\Client\Client
Note the httpClient argument. This must be an instance of the \Example\Http\Client\Client class.
The above does not work - the string "\Example\Http\Client\Client" is passed as the httpClient argument to the service.
What is the syntax for achieving the above by means of passing an instance of \Example\Http\Client\Client to the service constructor?
Create a private service. Here's what's written in the documentation:
If you use a private service as an argument to more than one other service, this will result in two different instances being used as the instantiation of the private service is done inline (e.g. new PrivateFooBar()).
services:
acme.services.exampleservice:
class: Acme\ExampleBundle\Services\ExampleService
arguments:
entityManager: "#doctrine.orm.entity_manager"
httpClient: acme.services.httpClient
acme.services.httpClient:
class: Example\Http\Client\Client
public: false
You will not be able to retrieve a private service from the container. From the outside it looks just as if you passed a regular object to the constructor of your service.
Related
Here is a method signature in the controller :
public function myAction(Request $request, SessionInterface $session, LoggerInterface $logger)
This is what I wrote in services.yml :
AppBundle\Controller\:
resource: '../../Controller'
public: true
tags: ['controller.service_arguments']
Normally, all of the parameters (request, session and logger) are automatically injected.
I call this action of the controller using Ajax but there is a problem for logger (request and session are well injected):
Controller myAction requires that you provide a value for the "$logger" argument
This happens for one method only because the ajax calls to some other methods of the controller work perfectly.
The only difference I can see using some debug tools is that the one that does not work has connection : close though the others have connection: keep-alive.
I dont see why it is done differently.
When I erase the LoggerInterface parameter, it works good and connection is keep-alive like the others.
Can you help ?
Symfony 3 platform
services:
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
I have a some Controllers, Twig Extensions and other classes that all need caching. I'm using Redis as a cache. Currently I setup a new RedisCache in each of these places, like so:
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
$this->cache = new RedisCache(RedisAdapter::createConnection(getenv('REDIS_URL')), 'ImageHelper');
}
But this creates many connections to the Redis backend, which I believe is not good for performance.
What is the best way to either share a RedisConnection/Client between my Controllers/extensions/classes, or even share the RedisCache, but keep namespacing ability?
I'm using Symfony 4.
You should make the Redis cache a service, and inject it into your controllers (or other dependents) later on. Refer to Symfony's dependency injection docs if needed.
Here's an example, you might need to fine-tune it later on:
# config/services.yaml
services:
redis_connection:
class: 'RedisConnection'
factory: ['RedisAdapter', createConnection]
arguments:
- '%env(REDIS_URL)%'
redis_cache:
class: 'RedisCache'
arguments:
- '#redis_connection'
Note that I don't know which namespaces your classes have, so you'd have to adjust the config accordingly.
And then you would also have to set up the framework to inject the service to your controllers (or other dependents):
# config/services.yaml
services:
# ...
App\Controller\ExampleController:
arguments:
- '#logger'
- '#redis_cache'
Also, you should update the controllers to accept the new argument in constructor, like so:
public function __construct(LoggerInterface $logger, RedisCache $cache)
{
$this->logger = $logger;
$this->cache = $cache;
}
Refer to the service container documentation if you have any questions.
I am starting to work with services in Symfony and therefore created the example service from the symfony documentation:
namespace AppBundle\Service;
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
public function __construct(LoggerInterface $logger){
}
public function getMessage()
{
$this->logger->info('Success!');
}
}
I call that service in my controller (I also have the use Statement:
: use AppBundle\Service\MessageGenerator;
$messageGenerator = $this->get(MessageGenerator::class);
$message = $messageGenerator->getMessage();
$this->addFlash('success', $message);
My service is defined in the services.yml file:
app.message_generator:
class: AppBundle\Service\MessageGenerator
public: true
so in my eyes I did everything exactly as described in the documentation and when calling:
php app/console debug:container app.message_generator
in my commandline I get my service:
Option Value
------------------ ------------------------------------
Service ID app.message_generator
Class AppBundle\Service\MessageGenerator
Tags -
Scope container
Public yes
Synthetic no
Lazy no
Synchronized no
Abstract no
Autowired no
Autowiring Types -
Now when I execute the controller function where I call my service I still get the error:
You have requested a non-existent service "appbundle\service\messagegenerator".
Any ideas?
Symfony is a bit confusing at naming: you retrieve the service by requesting it by its defined name: app.message_generator.
$messageGenerator = $this->get('app.message_generator');
Symfony has recently suggested switching from a give-name (app.message_generator) that you are defining the service as, to the class name (AppBundle\Service\MessageGenerator). They are both just 'a name' to call the service.
You are trying to use both, when only the given name is defined.
In the long term, it's suggested to use the ::class based name, and quite possibly allow the framework to find the classes itself, and configure them itself too. This means that, by default, all services are private, and are handled by the framework & it's service container.
In the meantime, while you are learning, you can either:
$messageGenerator = $this->get('app.message_generator');
or define explicitly define the service, and make it public, so it can be fetched with ->get(...) from the container.
# services.yml
AppBundle\Service\MessageGenerator:
class: AppBundle\Service\MessageGenerator
public: true
# php controller
$messageGenerator = $this->get(MessageGenerator::class);
or just injected automatically into the controller, when that is requested
public function __construct(LoggerInterface $logger, MessageGenerator $msgGen)
{
$this->messageGenerator = $msgGen;
}
public function getMessage()
{
$result = $this->messageGenerator->do_things(....);
$this->logger->info('Success!');
}
I'm creating a service that uses ImageWorkshop. In order to init a new image, I need to call:
$layer = ImageWorkshop::initFromPath(__DIR__.'/../path/to/myimage.jpg');
I'd like to inject ImageWorkshop as a dependency, but I can't figure out how to do this since it uses static methods. I know I could just call ImageWorkshop statically from my service, but I'm trying to declare my dependencies.
That's the perfect use case for service factories.
You declare your $layer as a service and create it with the static factory method in the service container.
services:
myimage_layer:
class: PHPImageWorkshop\Core\ImageWorkshopLayer
factory_class: PHPImageWorkshop\ImageWorkshop
factory_method: initFromPath
arguments:
- "%kernel.root_dir%/../path/to/myimage.jpg"
Now you can inject the myimage_layer service into your service as a service argument.
EDIT: If you need the ImageWorkshop directly to call them, but don't want to write ImageWorkshop::initFromPath('...') directly in your code, you can decouple it with the class name. It's not really useful, because ImageWorkshop is not directly replaceable, but it helps for mocking in tests.
services:
myimage_whatever:
class: Acme\Bundle\AcmeBundle\Image\Whatever
arguments:
- "PHPImageWorkshop\\ImageWorkshop"
Your service:
namespace Acme\Bundle\AcmeBundle\Image;
class Whatever
{
private $imageWorkshop;
public function __construct($imageWorkshop)
{
$this->imageWorkshop = $imageWorkshop;
}
public function doWhatever($path)
{
$layer = $this->imageWorkshop::initFromPath($path);
// ...
}
}
Beware yourself, $imageWorkshop is no instance. Instead it's a string containing the fully qualified class name of ImageWorkshop to call the static method on it. I hope this should work.
Reference for calling a static method on a string variable containing the class name: http://php.net/manual/en/language.oop5.static.php#example-214
I would create a wrapper class and implement the static class methods in it
e.g
Class ImageWorkshopWrapper
{
public function initFromPath($path)
{
ImageWorkshop::initFromPath($path);
}
}
and inject ImageWorkshopWrapper class
I am trying to send SMS to users who register in my website through Twilio, I got the vresh/twilio-bundle and it works fine.
I am trying to pass twilio instance to the event but I think I am missing something, here is what I am doing:
In config.yml i set the servide like this:
services:
registration.completed.listener:
class: Jaguar\AloBundle\EventListener\RegistrationEventListener
arguments:
entityManager: ["#doctrine.orm.voipswitch_entity_manager", "vresh_twilio"]
tags:
- { name: kernel.event_subscriber, event: performOnRegistrationCompleted }
I have declared the twilio config:
vresh_twilio:
sid: 'xxx'
authToken: 'xxx'
version: '2010-04-01'
retryAttempts: 3
Then, in my method I try to get the instance:
public function performOnRegistrationCompleted(UserEvent $event)
{
$twilio = $event->get('vresh_twilio');
}
But it fails...
Any help on this, please?
Thanks a lot!
There are a few issues with your service setup.
You are not actually passing the Twilio instance as you have no # sign preceding the service name. #vresh_twilio is a service, vresh_twilio is just a string.
You are passing in an associative array with a key of entityManager and a value that is also an array with the values of the service #doctrine.orm.voipswitch_entity_manager and the string vresh_twilio.
You're not passing the Twilio instance in your event you are building a listener with the Twilio instance in the constructor.
Your service should actually look like...
services:
registration.completed.listener:
class: Jaguar\AloBundle\EventListener\RegistrationEventListener
arguments:
entityManager: "#doctrine.orm.voipswitch_entity_manager"
twilio: "#vresh_twilio"
// Or
// - #doctrine.orm.voipswitch_entity_manager
// - #vresh_twilio
// Or
// [#doctrine.orm.voipswitch_entity_manager, #vresh_twilio]
//
// As they all mean the same thing and the keys aren't
// used in your actual service __construct
tags:
- { name: kernel.event_subscriber, event: performOnRegistrationCompleted }
This would mean your listener would then have a constructor to receive those services like..
protected $entityManager;
protected $twilio;
public function __conctruct(ObjectManager $entityManager, TwilioWrapper $twilio)
{
$this->entityManager = $entityManager;
$this->twilio = $twilio;
}
Meaning that you could then call it in your class using $this->twilio.
Also, from looking at the services that the Vresh\TwilioBundle creates it looks like the service that you would want to be injecting would be #twilio.api rather than #vresh_twilio as it doesn't seem to exist but I may be wrong there (I haven't used the bundle myself).