I am using the m6web_guzzle
bundle to register several http clients:
m6web_guzzlehttp:
clients:
myclient:
timeout: 3
headers:
"Accept": "application/json"
delay: 0
verify: false
I want to call a method on a service that it dynamically generates. In this case the generated service name is:
@m6web_guzzlehttp.guzzle.handlerstack.myclient
Here is what I do in my service constructor: (the 3rd parameter injected is '@m6web_guzzlehttp.guzzle.handlerstack.myclient')
/**
* @param array $parameters
* @param Client $client
* @param HandlerStack $handlerStack
*/
public function __construct(array $parameters, Client $client, HandlerStack $handlerStack)
{
$this->parameters = $parameters;
$this->client = $client;
$this->handlerStack->push(Middleware::retry([$this, 'retryDecider']));
}
So far, it works well, but how can I transfer the last line (the push
call) in my services.yml
file? Or another cleaner method to register this retry handler?
So compiler passes were mentioned before. That is one option.
But you can nearly express this also directly in your services definition. I say nearly, because you will need some kind of code as Symfony service definitions cannot (AFAIK) evaluate to a Closure - which is what we need for the Guzzle Middleware.
I wrote up this services.yml as an example:
m6web_guzzlehttp.guzzle.handlerstack.myclient:
class: GuzzleHttp\HandlerStack
factory: ['GuzzleHttp\HandlerStack', create]
retry_decider:
class: MyBundle\RetryDecider
factory: ['MyBundle\RetryDecider', createInstance]
retry_handler:
class: GuzzleHttp\Middleware
factory: ['GuzzleHttp\Middleware', retry]
arguments:
- '@retry_decider'
handlerstack_pushed:
parent: m6web_guzzlehttp.guzzle.handlerstack.myclient
calls:
- [push, ['@retry_handler']]
m6web_guzzlehttp.guzzle.handlerstack.myclient
- Your dynamic service - remove from example as you have this already created.retry_decider
- Your decider. We return a Closure in the createInstance
method. You can add more parameters if you need, just add arguments to your YML.retry_handler
- Here we compose the middleware using our deciderhandlerstack_pushed
- Here we push()
our handler into the stack, using the dynamic service as a parent service.Et voilà - we have the stack that the dynamic service defined, but pushed our retry middleware.
Here is the source for our decider:
<?php
namespace MyBundle;
class RetryDecider {
public static function createInstance() {
return function() {
// do your deciding here
};
}
}
--> You now have the service handlerstack_pushed
which is the complete Stack.
Please note that you could add m6web_guzzlehttp.guzzle.handlerstack.myclient
to parameters.yml
:
parameters:
baseHandlerStackService: m6web_guzzlehttp.guzzle.handlerstack.myclient
Then use that on handlerstack_pushed
:
handlerstack_pushed:
parent: "%baseHandlerStackService%"
calls:
- [push, ['@retry_handler']]
Just nicer like that ;-)
In your bundle's Extension.php
file, you can override the load method and add:
$definition = $container->getDefinition('m6web_guzzlehttp.guzzle.handlerstack.myclient');
$definition->addMethodCall('push', [Middleware::retry([$this, 'retryDecider'])]);
You can write a compiler pass that grabs the definition in question and adds the method call to it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With