csarrazi / CsaGuzzleBundle

A bundle integrating Guzzle >=4.0 in Symfony
250 stars 77 forks source link

Add documentation for making autowireable Guzzle client with custom class #236

Open rvanlaak opened 6 years ago

rvanlaak commented 6 years ago

Another use case to document; changing the client's class, and how to autowire that to another own service definition.

Possible content

The bundle can get used as "a factory" to create an autowireable class. That way the bundle puts the middleware (like logging / error reporting / timing) in place.

<?php

namespace Acme\Infra\Shopping;

class Client extends \GuzzleHttp\Client
{
}

csa_guzzle:
    clients:
        shopping:
            class: 'Acme\Infra\Shopping\Client'
            config:
                base_uri: '%shopping.base_uri%'

When someone wants to autowire that class in their own service definition:

services:
    Acme\Application\Shopping\Cart:
        arguments: ['@Acme\Infra\Shopping\Client']   

Important note:

For controller action argument autowiring this works perfectly, but for resolving service arguments on container compilation this would unfortunately give a "service not found" exception because the bundle would register the client to the container after it resolved the rest of the services.

A way the container can resolve / autowire is by aliasing the service this bundle creates:

services:
    Acme\Infra\Shopping\Client:
        alias: 'csa_guzzle.client.shopping'

Probably a solution that wouldn't need this additional service alias, is by somewhere changing compilation priorities. Can bundles do their work several steps earlier in the compilation process?

csarrazi commented 6 years ago

Thanks for the suggestion! I definitely agree, we should provide documentation for such use case.

In any case, if you wish to provide a doc PR, feel free to fork the project, and change the files in the src/Resources/doc folder :)

One thing though: is there any specific reason for extending the client, instead of wrapping it in a decorator class?

micayael commented 5 years ago

Hi @csarrazi. Is there some documentation about this?

csarrazi commented 5 years ago

Hy @micayael not yet. I'm still waiting for someone to make a PR for this, as I am a bit short on time, these days.

micayael commented 5 years ago

Thanks for reply @csarrazi.

Do you know if this is posible? I am trying multiple things but without success. if you could give me a little light on this I could help with the documentation

csarrazi commented 5 years ago

The main reason for that I wish to encourage composition instead of inheritance. Meaning that instead of extending the Guzzle client, it would be better to wrap the client in a class exposing only business logic.

micayael commented 5 years ago

@csarrazi I think I've accomplished with your explanation

I configure the client with own configuration in csa_guzzle.yaml or config.yml

csa_guzzle:
    profiler: '%kernel.debug%'
    clients:
        client1:
            config:
                base_uri: http://localhost:8000
                timeout: 3.0
                headers:
                    "Content-Type": application/json

I create a Symfony service wrapping my client in services.yaml

services:

    App\MyOwnClient:
        arguments: ['@csa_guzzle.client.client1']

My Symfony service use the internal client in MyOwnClient.php

<?php

namespace App;

use GuzzleHttp\Client;

class RulerClient
{
    private $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public function ownMethod(string $path, array $data)
    {
        $response = $this->client->post($path, [
            'json' => $data
        ]);

        return $response;
    }
}

Now I can use it and I have the symfony profile functional

csarrazi commented 5 years ago

👍