107 lines
5.3 KiB
Markdown
107 lines
5.3 KiB
Markdown
## Implementing custom API methods
|
|
|
|
You can use this feature if you need to group multiple API calls, or return something custom - not the API response as-is,
|
|
or, for example, to use new API methods without waiting for the implementation. For all of those cases, we provide a special
|
|
resource group in the client that will make it easy to implement a custom API method.
|
|
|
|
The main limitation of this mechanism is the lack of DTO. You will be limited to arrays inside custom methods. It is
|
|
_possible_ to use custom DTO's for the custom methods, but it is a little tricky - check the bottom of this page for the example.
|
|
|
|
Let's imagine that we have a special method with this route: `/api/v5/dialogs`. This method returns a list of the dialogs present in the system.
|
|
We cannot use it directly because it is not implemented in the client. However, we can implement it by ourselves using custom methods.
|
|
It will look like this:
|
|
|
|
```php
|
|
use RetailCrm\Api\Component\CustomApiMethod;
|
|
use RetailCrm\Api\Enum\RequestMethod;
|
|
use RetailCrm\Api\Factory\SimpleClientFactory;
|
|
use RetailCrm\Api\Interfaces\ApiExceptionInterface;
|
|
|
|
$client = SimpleClientFactory::createClient('https://test.simla.io', 'key');
|
|
$client->customMethods->register('dialogs', new CustomApiMethod(RequestMethod::GET, 'dialogs'));
|
|
|
|
try {
|
|
$dialogs = $client->customMethods->call('dialogs');
|
|
} catch (ApiExceptionInterface $exception) {
|
|
echo sprintf(
|
|
'Error from RetailCRM API (status code: %d): %s',
|
|
$exception->getStatusCode(),
|
|
$exception->getMessage()
|
|
);
|
|
|
|
if (count($exception->getErrorResponse()->errors) > 0) {
|
|
echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
echo 'Dialogs: ' . print_r($dialogs['dialogs'], true);
|
|
```
|
|
|
|
First, we need to register the custom method in the client. To do this we must give our method a name and an implementation.
|
|
The name will be used to call the method later using `call()` and the implementation is just a callable with the three
|
|
parameters: `RequestSenderInterface`, data array, and the context. Let's take a look at all three.
|
|
|
|
1. `RequestSenderInterface` is implemented by the `RequestSender` and contains three methods: `send`, `route` and `host`.
|
|
`send` is used to send the request, `route` will append the base URL to your method name, and the latter, `host`, will return
|
|
hostname from the base URL.
|
|
2. The data array contains any data that has been provided to the callable during the method call. It will be encoded as
|
|
a query string and stored either as the URL params or as the form body (query string is used for GET and DELETE requests).
|
|
3. The context contains any information provided to the callable during the method call. It is not checked by the client.
|
|
However, it is always must be of array type.
|
|
|
|
As you can see, we're using `CustomApiMethod` as a callable in the example. The `CustomApiMethod` is a simple invokable
|
|
wrapper that can be used to simplify the registration of the new methods. The boilerplate code in the previous example is
|
|
already implemented inside the `CustomApiMethod` component.
|
|
|
|
It is easier to understand how the callable should work and how the `CustomApiMethod` works by looking at the registration example.
|
|
It works exactly like the previous example, but without the `CustomApiMethod` component:
|
|
|
|
```php
|
|
use RetailCrm\Api\Enum\RequestMethod;
|
|
use RetailCrm\Api\Interfaces\RequestSenderInterface;
|
|
|
|
$client->customMethods->register('dialogs', function (RequestSenderInterface $sender, $data, array $context) {
|
|
return $sender->send(RequestMethod::GET, $sender->route('dialogs'), $data);
|
|
});
|
|
```
|
|
|
|
The data here is not defined as array because it can be anything you like. You can use any serializer to serialize the data
|
|
and deserialize the response. `CustomApiMethod` only supports array out-of-box, but your handlers can utilize any
|
|
data types.
|
|
|
|
The base URL inside the client is always represented as a URL with the version suffix. It should be always kept in mind
|
|
while using the `route` method:
|
|
|
|
```php
|
|
// This code is being executed inside the custom method callable.
|
|
// Base URL is http://test.simla.io/api/v5
|
|
|
|
$settingsRoute = $sender->route('settings');
|
|
$host = $sender->host();
|
|
|
|
echo $settingsRoute; // prints "https://test.simla.io/api/v5/settings" - the slash is inserted by the route() method.
|
|
echo $host; // prints "test.simla.io"
|
|
```
|
|
|
|
Any registered custom method can be called using the `__call` magic method:
|
|
|
|
```php
|
|
// Both calls below works the same.
|
|
|
|
$client->customMethods->call('dialogs', ['param' => 'value'], ['contextParam' => 'contextValue']);
|
|
$client->customMethods->dialogs(['param' => 'value'], ['contextParam' => 'contextValue']);
|
|
```
|
|
|
|
The last notable difference from the regular methods lies inside events. You won't be able to get request or response models
|
|
from the events, but you can extract the response array using the `getResponseArray()` method.
|
|
|
|
## Using DTOs with the custom methods
|
|
|
|
You can use DTOs with the custom methods. To do that you just need to serialize the request models inside the method callback
|
|
before sending the request and deserialize the response before returning it. We already made a great example project that
|
|
demonstrates the DTO usage alongside custom methods.
|
|
|
|
[**DTOs with the custom methods - sample project**](examples/custom-api-methods-with-dto)
|