256 lines
9.6 KiB
Markdown
256 lines
9.6 KiB
Markdown
|
## Instantiation
|
||
|
|
||
|
There are several ways to instantiate the Client. Let's take look at them: from the simplest one to the most complex.
|
||
|
|
||
|
* [Instantiation via `SimpleClientFactory`](#instantiation-via-simpleclientfactory)
|
||
|
* [Instantiation via `ClientFactory`](#instantiation-via-clientfactory)
|
||
|
+ [Simple example](#simple-example)
|
||
|
+ [Abstract example](#abstract-example)
|
||
|
+ [Real-world example (Symfony)](#real-world-example-symfony)
|
||
|
* [Instantiation via `ClientBuilder`](#instantiation-via-clientbuilder)
|
||
|
|
||
|
### Instantiation via `SimpleClientFactory`
|
||
|
|
||
|
It's the easiest way to instantiate an API client. You can use this method if you just want to use API client without
|
||
|
thinking much about integration with the app or framework.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
```php
|
||
|
use RetailCrm\Api\Factory\SimpleClientFactory;
|
||
|
|
||
|
$client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'key');
|
||
|
```
|
||
|
|
||
|
That's it. You can use `$client` to make requests. However, even this simple method allows you to pass the `CacheItemPoolInterface` instance
|
||
|
(`symfony/cache` is being used by default) or path to the cache directory. The cache is being used by the client to store
|
||
|
annotations which omits the necessity to parse them each time you send a request.
|
||
|
|
||
|
Example with the `CacheItemPoolInterface` instance (we're using Redis as cache here):
|
||
|
|
||
|
```php
|
||
|
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||
|
use RetailCrm\Api\Enum\CacheDirectories;
|
||
|
use RetailCrm\Api\Factory\SimpleClientFactory;
|
||
|
|
||
|
$redis = new Redis();
|
||
|
$redis->connect('127.0.0.1', 6379);
|
||
|
|
||
|
$client = SimpleClientFactory::createWithCache(
|
||
|
'https://test.retailcrm.pro',
|
||
|
'key',
|
||
|
new RedisAdapter($redis, CacheDirectories::DIR_NAME)
|
||
|
);
|
||
|
```
|
||
|
|
||
|
`CacheDirectories::DIR_NAME` is optional here. You can use any other namespace you want. By default, `CacheDirectories::DIR_NAME`
|
||
|
will be used as a namespace by default if the cache instance was created by the client automatically.
|
||
|
|
||
|
It's easier to use `SimpleClientFactory::createWithCacheDir` if you just want to specify your directory for cache instead of
|
||
|
default temporary directory:
|
||
|
|
||
|
```php
|
||
|
use RetailCrm\Api\Factory\SimpleClientFactory;
|
||
|
|
||
|
$client = SimpleClientFactory::createWithCacheDir('https://test.retailcrm.pro', 'key', __DIR__ . '/cache');
|
||
|
```
|
||
|
|
||
|
You can find more details about `SimpleClientFactory` in the documentation:
|
||
|
* [`SimpleClientFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-SimpleClientFactory.html)
|
||
|
|
||
|
### Instantiation via `ClientFactory`
|
||
|
|
||
|
The main difference between `SimpleClientFactory` and `ClientFactory` is the fact that the second factory is stateful.
|
||
|
It allows you to better integrate this client with your application. Consider the following: you may just set all global client
|
||
|
dependencies into the `ClientFactory` during the dependency container instantiation, and use the factory after that
|
||
|
to instantiate any number of clients you want.
|
||
|
|
||
|
#### Simple example
|
||
|
|
||
|
Here's an example of `ClientFactory` usage without any integration with the framework or the app:
|
||
|
|
||
|
```php
|
||
|
use RetailCrm\Api\Factory\ClientFactory;
|
||
|
use League\Event\EventDispatcher;
|
||
|
|
||
|
$eventDispatcher = new EventDispatcher();
|
||
|
$factory = new ClientFactory();
|
||
|
$factory
|
||
|
->setCacheDir('/tmp/retailcrm_cache')
|
||
|
->setEventDispatcher($eventDispatcher);
|
||
|
|
||
|
$client = $factory->createClient('https://test.retailcrm.pro', 'key');
|
||
|
```
|
||
|
|
||
|
Doesn't look that impressive, right? That's because `ClientFactory` is not supposed to be used just like that.
|
||
|
It won't give you any benefits in comparison to `SimpleClientFactory` with such usage.
|
||
|
|
||
|
Let's take a look at the more complex example. We'll use `league/container` here to demonstrate how you can integrate
|
||
|
`ClientFactory` with the DI you're using.
|
||
|
|
||
|
#### Abstract example
|
||
|
|
||
|
Let's assume that you have a specific service that should instantiate a Client for some internal purposes.
|
||
|
Here it is:
|
||
|
|
||
|
```php
|
||
|
namespace App\Services;
|
||
|
|
||
|
use RetailCrm\Api\Interfaces\ClientFactoryInterface;
|
||
|
use Throwable;
|
||
|
|
||
|
class ClientFactoryDependentService
|
||
|
{
|
||
|
/** @var ClientFactoryInterface */
|
||
|
private $clientFactory;
|
||
|
|
||
|
public function __construct(ClientFactoryInterface $clientFactory)
|
||
|
{
|
||
|
$this->clientFactory = $clientFactory;
|
||
|
}
|
||
|
|
||
|
public function isApiAccessible(string $apiUrl, string $apiKey): bool
|
||
|
{
|
||
|
try {
|
||
|
$client = $this->clientFactory->createClient($apiUrl, $apiKey);
|
||
|
$client->api->apiVersions();
|
||
|
|
||
|
return true;
|
||
|
} catch (Throwable $throwable) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And you have a controller which returns a JSON response with the API availability flag. It requires the service above.
|
||
|
|
||
|
```php
|
||
|
namespace App\Controller;
|
||
|
|
||
|
use Some\Framework\Specific\Annotations\Route;
|
||
|
use Some\Framework\Specific\Controller;
|
||
|
use Some\Framework\Specific\Response;
|
||
|
|
||
|
class HealthCheckController extends Controller
|
||
|
{
|
||
|
/** @var ClientFactoryDependentService */
|
||
|
private $service;
|
||
|
|
||
|
public function __construct(ClientFactoryDependentService $service)
|
||
|
{
|
||
|
$this->service = $service;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @Route("/healthcheck")
|
||
|
*/
|
||
|
public function healthCheckAction(): Response
|
||
|
{
|
||
|
return $this->responseJson([
|
||
|
'is_api_accessible' => $this->service->isApiAccessible($_GET['apiUrl'], $_GET['apiKey'])
|
||
|
]);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
During the container configuration you configure the container to instantiate proper `ClientFactory` once. After that
|
||
|
you can use the factory in any service you want. Here's an example with the `league/container`:
|
||
|
|
||
|
```php
|
||
|
use RetailCrm\Api\Factory\ClientFactory;
|
||
|
use RetailCrm\Api\Interfaces\ClientFactoryInterface;
|
||
|
use League\Container\Container;
|
||
|
use League\Event\EventDispatcher;
|
||
|
use Psr\Cache\CacheItemPoolInterface;
|
||
|
use Psr\EventDispatcher\EventDispatcherInterface;
|
||
|
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||
|
use App\Services\ClientFactoryDependentService;
|
||
|
use App\Controller\HealthCheckController;
|
||
|
|
||
|
$container = new Container();
|
||
|
|
||
|
$container->add(CacheItemPoolInterface::class, new FilesystemAdapter('test_app'));
|
||
|
$container->add(EventDispatcherInterface::class, EventDispatcher::class);
|
||
|
$container->add(ClientFactoryInterface::class, ClientFactory::class)
|
||
|
->addMethodCalls([
|
||
|
'setCache' => [CacheItemPoolInterface::class],
|
||
|
'setEventDispatcher' => [EventDispatcherInterface::class],
|
||
|
]);
|
||
|
$container->add(ClientFactoryDependentService::class)->addArgument(ClientFactoryInterface::class);
|
||
|
$container->add(HealthCheckController::class)->addArgument(ClientFactoryDependentService::class);
|
||
|
```
|
||
|
|
||
|
That's it! Now your controller is using the underlying service to form a response. The service is using `ClientFactory`
|
||
|
which is being instantiated only once by the container. `ClientFactory` can be injected into other services as well, and it
|
||
|
will be the same instance.
|
||
|
|
||
|
#### Real-world example (Symfony)
|
||
|
|
||
|
You can use this exact service definition to instantiate a `ClientFactory` instance:
|
||
|
|
||
|
```yaml
|
||
|
RetailCrm\Api\Interfaces\ClientFactoryInterface:
|
||
|
class: 'RetailCrm\Api\Factory\ClientFactory'
|
||
|
calls:
|
||
|
- setCacheDir: ['%kernel.cache_dir%']
|
||
|
- setEventDispatcher: ['@event_dispatcher']
|
||
|
```
|
||
|
|
||
|
That's it. Now you can autowire `ClientFactoryInterface` in your services. It also allows you to mock the factory itself
|
||
|
in the tests using `services_test.yml`.
|
||
|
|
||
|
You can find more details about `ClientFactory` in the documentation:
|
||
|
* [`ClientFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-ClientFactory.html)
|
||
|
|
||
|
### Instantiation via `ClientBuilder`
|
||
|
|
||
|
`ClientBuilder` is the most powerful, and most complex method of client instantiation. It allows you to replace any client's
|
||
|
external dependencies with your own implementations. Here's an example of `ClientBuilder` usage:
|
||
|
|
||
|
```php
|
||
|
use Http\Client\Curl\Client as CurlClient;
|
||
|
use League\Event\EventDispatcher;
|
||
|
use Nyholm\Psr7\Factory\Psr17Factory;
|
||
|
use RetailCrm\Api\Builder\ClientBuilder;
|
||
|
use RetailCrm\Api\Builder\FormEncoderBuilder;
|
||
|
use RetailCrm\Api\Component\Transformer\RequestTransformer;
|
||
|
use RetailCrm\Api\Component\Transformer\ResponseTransformer;
|
||
|
use RetailCrm\Api\Factory\ApiExceptionFactory;
|
||
|
use RetailCrm\Api\Factory\RequestPipelineFactory;
|
||
|
use RetailCrm\Api\Factory\ResponsePipelineFactory;
|
||
|
use RetailCrm\Api\Handler\Request\HeaderAuthenticatorHandler;
|
||
|
|
||
|
$eventDispatcher = new EventDispatcher();
|
||
|
$psr17Factory = new Psr17Factory();
|
||
|
$httpClient = new CurlClient();
|
||
|
|
||
|
$builder = new ClientBuilder();
|
||
|
$formEncoder = (new FormEncoderBuilder())->setCacheDir('cache')->build();
|
||
|
$client = $builder->setApiUrl('https://test.retailcrm.pro')
|
||
|
->setAuthenticatorHandler(new HeaderAuthenticatorHandler('apiKey'))
|
||
|
->setFormEncoder($formEncoder)
|
||
|
->setHttpClient($httpClient)
|
||
|
->setRequestTransformer(new RequestTransformer(
|
||
|
RequestPipelineFactory::createDefaultPipeline(
|
||
|
$formEncoder,
|
||
|
$psr17Factory, // PSR-17 UriFactoryInterface
|
||
|
$psr17Factory, // PSR-17 RequestFactoryInterface
|
||
|
$psr17Factory // PSR-17 StreamFactoryInterface
|
||
|
)
|
||
|
))
|
||
|
->setResponseTransformer(new ResponseTransformer(
|
||
|
ResponsePipelineFactory::createDefaultPipeline(
|
||
|
$formEncoder->getSerializer(),
|
||
|
new ApiExceptionFactory(),
|
||
|
$eventDispatcher
|
||
|
)
|
||
|
))->build();
|
||
|
```
|
||
|
|
||
|
You can find more details about this in the documentation:
|
||
|
* [`ClientBuilder`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Builder-ClientBuilder.html)
|
||
|
* [`FormEncoderBuilder`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Builder-FormEncoderBuilder.html)
|
||
|
* [`RequestPipelineFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-RequestPipelineFactory.html)
|
||
|
* [`ResponsePipelineFactory`](https://retailcrm.github.io/api-client-php/classes/RetailCrm-Api-Factory-ResponsePipelineFactory.html)
|