Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a method on a service created dynamically by a bundle

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?

like image 333
COil Avatar asked Sep 15 '17 10:09

COil


3 Answers

So compiler passes were mentioned before. That is one option.

Use factories to create instances

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']]

What is what?

  • 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 decider
  • handlerstack_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.

Configuring more

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

like image 121
narcoticfresh Avatar answered Nov 06 '22 01:11

narcoticfresh


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'])]);
like image 38
Asha Avatar answered Nov 06 '22 01:11

Asha


You can write a compiler pass that grabs the definition in question and adds the method call to it.

like image 2
xabbuh Avatar answered Nov 06 '22 01:11

xabbuh