1
0
mirror of synced 2024-11-23 13:56:06 +03:00
api-client-php/doc/customization/implementing_custom_api_methods.md

5.3 KiB

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:

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:

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:

// 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:

// 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