В сегодняшней статье мы будем писать свой пакет для Laravel.
Пакет для запросов адреса и получения информации по адресу через API сервиса Dadata.
Что будет уметь наш пакет:
- Отправлять адрес на удаленное API и возвращать результат ( Будем использовать PSR-7 и Guzzle), формат ответа будет выглядеть следующем образом при успехе:
{
"data": {
"suggestions": [ {
"region": "Москва",
"value": "г Москва, ул Лубянка Б., д 12", "coordinates": {
"geo_lat": "55.7618518",
"geo_lon": "37.6284306" }
},
...
]
},
"success": true
}
Или так в случае ошибки:
{
"data": [
{
"code": 1020,
"message": "Access forbidden"
},
...
],
"success": false
}
Давайте начинать!
Что будем использовать?
В качеству примера мы будем использовать https://dadata.ru/api/clean/address/ первые 100 записей бесплатно, далее следуя тарифу. Для тестов нам хватит бесплатных записей.
Шаг 1 - Создаем composer.json
Первым делом нам нужно создать файл с зависимостями и подключить некоторые из них (например Guzzle). Создадим каталог client и выполним в нем команду:
composer init
В итоге в терминале вы увидите что-то типо такого:
Далее на всех шагах просто нажимайте Enter и в итоге мы получим созданный файл composer.json примерно со следующим содержимым:
{
"name": "stanislavboyko/client",
"authors": [
{
"name": "stanislavboyko",
"email": "myemail@gmail.com"
}
],
"require": {}
}
Наименование пакета и автора соответственно будут различаться. Давайте теперь добавим несколько директив. Сразу привету итоговую версию данного файла, а потом мы его немного разберём:
{
"name": "stanislavboyko/client",
"description": "Laravel Dadadta example client",
"homepage": "https://stanislavboyko.ru/",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "stanislavboyko",
"email": "mzcoding@gmail.com"
}
],
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"Stanislavboyko\\Client\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Stanislavboyko\\Client\\Tests\\": "tests/"
}
},
"require": {
"php": ">=7.1",
"ext-curl": "*",
"ext-json": "*"
},
"require-dev": {
"phpunit/phpunit": "^7.0",
"vlucas/phpdotenv": "^3.6"
},
"extra": {
"laravel": {
"providers": [
"Stanislavboyko\\Client\\Laravel\\ClientServiceProvider"
],
"aliases": {
"DDataClient": "Stanislavboyko\\Client\\Laravel\\ClientFacade"
}
}
}
}
Теперь рассмотрим самые важные директивы данного файла!
minimum-stability - Данная директива указывает уровень стабильности проекта, по умолчанию stable. Другими словами, если в вашей версии пакета будет указан stable, а в вашей версии фреймворка dev он не установит этот пакет.
autoload и autoload-dev - Данные директивы содержат относительный путь от корня пакета к каталогам автозагрузки классов.
require - Здесь мы указываем зависимости которые обязательно для работы нашей библиотеки, так-же указываются необходимая версия PHP и необходимые расширения.
Так-же мы установим Guzzle для запросов.
require-dev - Здесь указаны зависимости доступные только для разработки, то есть они устанавливаются только в режиме разработки.
Здесь мы подключаем PhpUnit для тестов и библиотеку vlucas/phpdotenv для работы с .env файлами.
extra - Данная директива служит инструкцией для фреймворка по подключению Провайдера и Фасада пакета.
Теперь создадим каталоги src и tests, а так-же каталог config. После этого в терминале введем, чтобы установить необходимые зависимости:
composer install
Шаг 2 - создаем базовый клиент
После этого у вас так-же появится каталог vendor со всеми зависимостями и так-же добавится файл composer.lock. Давайте сразу в корне нашего пакета создадим файл .gitignore и добавим в него несколько строк:
/vendor/
/composer.lock
Теперь давайте опишем файл конфигурации, в каталоге config создадим файл dclient.php и добавим туда следующий код:
<?php
return [
'api_key' => '', //your api key for dadata
'secret_key' => '',
'base_url' => '',
'timeout' => 20,
];
Вы можете зарегистрироваться и добавить свои ключи, при регистрации Дадата зачисляет 10 рублей на счет, этого хватит на 100 запросов.
Теперь в каталоге src создадим 4 каталога, это Laravel, Dadata,Contract и каталог Exception. Теперь создадим наши СервисПровайдер и Фасад ( файлы ClientServiceProvider.php и ClientFacade.php соответственно).
Сперва опишем наш провайдер ClientServiceProvider.php:
<?php
namespace Stanislavboyko\Client\Laravel;
use Illuminate\Support\ServiceProvider;
use Stanislavboyko\Client\Dadata\Client;
class ClientServiceProvider extends ServiceProvider
{
public function __construct($dispatcher = null)
{
parent::__construct($dispatcher);
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__ . '/../../config/dclient.php' => config_path('mzcoding-client.php'),
]);
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__ . '/../../config/dclient.php', 'dclient'
);
$this->app->bind(Client::class);
}
}
Здесь мы регистрирует конфигурационный файл и наш основной класс клиента ( для dadadta) его мы опишем немного позже, а пока посмотрим на фасад ClientFacade.php:
<?php
namespace Stanislavboyko\Client\Laravel;
use Illuminate\Support\Facades\Facade;
use Stanislavboyko\Client\Dadata\Client;
class ClientFacade extends Facade
{
/**
* @return string
* @see \Stanislavboyko\Client\Client
*/
protected static function getFacadeAccessor(): string
{
return Client::class;
}
}
Сперва создадим и опишем интерфейс ClientInterface.php:
<?php
namespace Stanislavboyko\Client\Contract;
use Stanislavboyko\Client\Client;
use Stanislavboyko\Client\Response;
interface ClientInterface
{
/**
* @return \GuzzleHttp\Client|null
*/
function getClient(): ?\GuzzleHttp\Client;
function clientInit(array $config = []): \GuzzleHttp\Client;
/**
* @param array $headers
* @return Client
*/
function setHeaders(array $headers = []): Client;
/**
* @param $url
* @param string $method
* @param array $params
* @return Response
*/
function request($url, $method = 'GET', array $params = []): Response;
}
Теперь в каталоге src создадим файл Client.php это будет наш основной клиент.
<?php
namespace Stanislavboyko\Client;
use Stanislavboyko\Client\Config;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Client as GuzzleClient;
/**
* @method getReasonPhrase()
*/
class Client
{
protected $client, $config;
protected $params, $headers, $auth = [];
public function __construct(Config $config, bool $init = true)
{
$this->config = $config->get();
$this->headers = $config->guzzle()['headers'];
if($init) {
return $this->clientInit($config->guzzle());
}
}
/**
* Get current client instance
*
* @return null|\GuzzleHttp\Client
*/
public function getClient(): ?\GuzzleHttp\Client
{
return $this->client;
}
public function clientInit(array $config = []): self
{
$this->client = new \GuzzleHttp\Client($config);
return $this;
}
/**
* @param array $headers
* @return Client
*/
public function setHeaders(array $headers = []): Client
{
$this->headers = array_merge($this->headers, $headers);
return $this;
}
/**
* @return array
*/
public function getHeaders(): array
{
return $this->headers;
}
/**
* @param array $credentials
* @return Client
*/
public function setAuth(array $credentials = []): Client
{
$this->auth = $credentials;
return $this;
}
/**
* @return array
*/
public function getAuth(): array
{
return (array)$this->auth;
}
/**
* @param string $url
* @param string $method
* @param array $params
* @return Response
*/
public function request(string $url, string $method = 'GET', array $params = []): Response
{
$options = [];
$method = strtoupper($method);
if(isset($params[0])) {
$options['json'] = [$params[0]];
unset($params[0]);
}
if( $this->auth ) {
$options['auth'] = $this->auth;
}
if((bool)$this->config['debug']) {
$options['debug'] = (bool)$this->config['debug'];
}
$response = $this->client->request($method, $this->config['base_url'] . "/" . $url, $options);
return new Response($response);
}
}
Теперь опишем файлы Config и Response также находящиеся в каталоге src.
<?php
namespace Stanislavboyko\Client;
use Stanislavboyko\Client\Exception;
use Stanislavboyko\Client\Exception\ClientException;
class Config
{
protected $params = [];
public function __construct(array $parameters = [])
{
$this->params = $parameters;
}
public function set(array $parameters): void
{
$this->params = array_merge($this->params, $parameters);
}
public function get(): array
{
return $this->params;
}
public function first(string $name)
{
if(!isset($this->params[$name])) {
if($name === 'track_redirects') {
return false;
}
if($name === 'user_agent') {
return 'testing/1.0';
}
throw new ClientException('Parameter ' . $name . ' not found');
}
return $this->params[$name];
}
public function guzzle(): array
{
$options = [
'base_uri' => $this->first('base_url'),
'timeout' => $this->first('timeout'),
'track_redirects' => $this->first('track_redirects'),
'debug' => $this->first('debug'),
'headers' => [
'User-Agent' => $this->first('user_agent'),
'Content-Type' => "application/json",
"Accept" => "application/json",
"Authorization" => "Token " . $this->first('api_key'),
"X-Secret" => $this->first('secret_key')
]
];
return $options;
}
}
И так-же файл Response.php
<?php
namespace Stanislavboyko\Client;
use GuzzleHttp\Psr7\Response as GuzzleResponse;
use Stanislavboyko\Client\Contract\ResponseInterface;
class Response implements ResponseInterface
{
private $response;
/**
* Response constructor.
* @param Response $response
*/
public function __construct(GuzzleResponse $response)
{
$this->response = $response;
}
/**
* @return \App\Classes\Client\Guzzle\Response
*/
public function get(): GuzzleResponse
{
return $this->response;
}
/**
* @return mixed
*/
public function getBody()
{
return json_decode($this->get()->getBody(), true);
}
}
В заключении нам осталось описать файл который непосредственно взаимодействует с Dadata в каталоге src который содержит файл Dadata/Client.php со следующим содержимым:
<?php
namespace Stanislavboyko\Client\Dadata;
use GuzzleHttp\Exception\ClientException;
use Stanislavboyko\Client\Config;
use Stanislavboyko\Client\Client as BaseClient;
class Client
{
/**
* Return Dadata API client object
*
* @param string $url
* @param string $method
* @param array $params
*
* @return BaseClient
*/
public function getClient(string $url, string $method, array $params = []): BaseClient
{
$parameters = config('dclient');
if(!$parameters) {
$parameters = include __DIR__ . '/../../config/dclient.php';
}
$config = new Config($parameters);
$response = new BaseClient($config);
$res = $response->request($url, $method, $params);
dd($res->getBody());
$code = $response->getStatusCode();
if($code !== 200 || $code !== 201) {
throw new ClientException("Error exception: " . $code);
}
if ($code !== 429) {
throw new ClientException($code . ' ' . $response->getReasonPhrase());
}
return $response;
}
}
На этом все, теперь мы можем вызывать наш клиент через фасад:
Route::get('/', function () {
$client = DDataClient::getClient('clean/address', 'POST', ["мск суханка 24"]);
dd($client);
});
Весь код пакеты вы можете посмотреть и скачать по адресу: https://gitlab.com/mzcoding/client
0 комментариев