1. Глубже в детали
  2. HTTP-клиент

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

Laravel предоставляет выразительный, минимальный API вокруг клиента Guzzle HTTP, позволяя быстро выполнять исходящие HTTP-запросы для взаимодействия с другими веб-приложениями. Обертка Laravel вокруг Guzzle сосредоточена на его наиболее распространенных случаях использования и прекрасном опыте разработчика.

Прежде чем начать, убедитесь, что у вас установлен пакет Guzzle в качестве зависимости вашего приложения. По умолчанию Laravel автоматически включает эту зависимость. Однако, если вы ранее удалили пакет, вы можете установить его снова с помощью Composer:

composer require guzzlehttp/guzzle

Осуществление Запросов

Для выполнения запросов вы можете использовать методы head, get, post, put, patch и delete, предоставленные фасадом Http. Давайте сначала рассмотрим, как выполнить базовый запрос GET к другому URL:

use Illuminate\Support\Facades\Http;
 
$response = Http::get('http://example.com');

Метод get возвращает экземпляр Illuminate\Http\Client\Response, который предоставляет разнообразие методов, которые можно использовать для анализа ответа:

$response->body() : string;
$response->json($key = null, $default = null) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$response->status() : int;
$response->successful() : bool;
$response->redirect(): bool;
$response->failed() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;

Объект Illuminate\Http\Client\Response также реализует интерфейс ArrayAccess PHP, что позволяет вам напрямую получать доступ к данным ответа JSON:

return Http::get('http://example.com/users/1')['name'];

Помимо методов ответа, перечисленных выше, следующие методы можно использовать для определения, имеет ли ответ заданный код состояния:

$response->ok() : bool; // 200 OK
$response->created() : bool; // 201 Created
$response->accepted() : bool; // 202 Accepted
$response->noContent() : bool; // 204 No Content
$response->movedPermanently() : bool; // 301 Moved Permanently
$response->found() : bool; // 302 Found
$response->badRequest() : bool; // 400 Bad Request
$response->unauthorized() : bool; // 401 Unauthorized
$response->paymentRequired() : bool; // 402 Payment Required
$response->forbidden() : bool; // 403 Forbidden
$response->notFound() : bool; // 404 Not Found
$response->requestTimeout() : bool; // 408 Request Timeout
$response->conflict() : bool; // 409 Conflict
$response->unprocessableEntity() : bool; // 422 Unprocessable Entity
$response->tooManyRequests() : bool; // 429 Too Many Requests
$response->serverError() : bool; // 500 Internal Server Error

Шаблоны URI

HTTP-клиент также позволяет вам создавать URL-адреса запросов с использованием спецификации шаблонов URI. Чтобы определить параметры URL, которые можно расширить с использованием вашего шаблона URI, вы можете использовать метод withUrlParameters:

Http::withUrlParameters([
'endpoint' => 'https://laravel.com',
'page' => 'docs',
'version' => '9.x',
'topic' => 'validation',
])->get('{+endpoint}/{page}/{version}/{topic}');

Дамп Запросов

Если вы хотите вывести исходный экземпляр запроса перед его отправкой и завершить выполнение сценария, вы можете добавить метод dd в начало вашего определения запроса:

return Http::dd()->get('http://example.com');

Данные Запроса

Конечно, обычно при выполнении запросов POST, PUT и PATCH отправляются дополнительные данные с запросом, поэтому эти методы принимают массив данных в качестве второго аргумента. По умолчанию данные будут отправлены с использованием типа контента application/json:

use Illuminate\Support\Facades\Http;
 
$response = Http::post('http://example.com/users', [
'name' => 'Steve',
'role' => 'Network Administrator',
]);

Параметры Запроса GET

При выполнении запросов GET вы можете либо добавить строку запроса к URL напрямую, либо передать массив пар ключ / значение в качестве второго аргумента метода get:

$response = Http::get('http://example.com/users', [
'name' => 'Taylor',
'page' => 1,
]);

Также можно использовать метод withQueryParameters:

Http::retry(3, 100)->withQueryParameters([
'name' => 'Taylor',
'page' => 1,
])->get('http://example.com/users')

Отправка Запросов с Кодировкой URL Формы

Если вы хотите отправить данные с использованием типа контента application/x-www-form-urlencoded, вы должны вызвать метод asForm перед выполнением запроса:

$response = Http::asForm()->post('http://example.com/users', [
'name' => 'Sara',
'role' => 'Privacy Consultant',
]);

Отправка Тела Запроса Raw

Можно использовать метод withBody, если вы хотите предоставить сырое тело запроса при выполнении запроса. Тип контента можно указать вторым аргументом метода:

$response = Http::withBody(
base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');

Запросы Multi-Part

Если вы хотите отправить файлы в виде многократных запросов, вы должны вызвать метод attach перед выполнением запроса. Этот метод принимает имя файла и его содержимое. При необходимости вы можете предоставить третий аргумент, который будет считаться именем файла:

$response = Http::attach(
'attachment', file_get_contents('photo.jpg'), 'photo.jpg'
)->post('http://example.com/attachments');

Вместо передачи сырого содержимого файла вы можете передать потоковый ресурс:

$photo = fopen('photo.jpg', 'r');
 
$response = Http::attach(
'attachment', $photo, 'photo.jpg'
)->post('http://example.com/attachments');

Заголовки

Заголовки могут быть добавлены к запросам с использованием метода withHeaders. Этот метод withHeaders принимает массив пар ключ / значение:

$response = Http::withHeaders([
'X-First' => 'foo',
'X-Second' => 'bar'
])->post('http://example.com/users', [
'name' => 'Taylor',
]);

Можно использовать метод accept для указания типа контента, который ваше приложение ожидает в ответ на запрос:

$response = Http::accept('application/json')->get('http://example.com/users');

Для удобства вы можете использовать метод acceptJson, чтобы быстро указать, что ваше приложение ожидает тип контента application/json в ответ на запрос:

$response = Http::acceptJson()->get('http://example.com/users');

Метод withHeaders объединяет новые заголовки с существующими заголовками запроса. При необходимости вы можете полностью заменить все заголовки с использованием метода replaceHeaders:

$response = Http::withHeaders([
'X-Original' => 'foo',
])->replaceHeaders([
'X-Replacement' => 'bar',
])->post('http://example.com/users', [
'name' => 'Taylor',
]);

Аутентификация

Можно указать учетные данные для базовой и дайджест-аутентификации, используя методы withBasicAuth и withDigestAuth соответственно:

// Базовая аутентификация... $response = Http::withBasicAuth('[email protected]', 'secret')->post(/* ... */);

// Дайджест-аутентификация... $response = Http::withDigestAuth('[email protected]', 'secret')->post(/* ... */);

Маркеры Dearer

Если вы хотите быстро добавить маркер доступа к заголовку Authorization запроса, вы можете использовать метод withToken:

$response = Http::withToken('token')->post(/* ... */);

Тайм-аут

Метод timeout может использоваться для указания максимального количества секунд, ожидая ответа. По умолчанию HTTP-клиент завершит работу через 30 секунд:

$response = Http::timeout(3)->get(/* ... */);

Если установленный таймаут превышен, будет сгенерировано исключение Illuminate\Http\Client\ConnectionException.

Можно указать максимальное количество секунд ожидания при попытке подключения к серверу с использованием метода connectTimeout:

$response = Http::connectTimeout(3)->get(/* ... */);

Повторы

Если вы хотите, чтобы HTTP-клиент автоматически повторил запрос в случае ошибки клиента или сервера, вы можете использовать метод retry. Метод retry принимает максимальное количество попыток выполнения запроса и количество миллисекунд, которое Laravel должна ждать между попытками:

$response = Http::retry(3, 100)->post(/* ... */);

При необходимости вы можете передать третий аргумент методу retry. Третий аргумент должен быть вызываемым, которое определит, следует ли фактически предпринимать попытки повторения. Например, вы можете захотеть повторить запрос только в случае, если начальный запрос сталкивается с ConnectionException:

use Exception;
use Illuminate\Http\Client\PendingRequest;
 
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->post(/* ... */);

Если попытка запроса завершится неудачей, возможно, вы захотите внести изменение в запрос перед новой попыткой. Это можно сделать, модифицировав аргумент запроса, предоставленный замыканию, предоставленному методу retry. Например, вы можете хотеть повторить запрос с новым токеном авторизации, если первая попытка вернула ошибку аутентификации:

use Exception;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\RequestException;
 
$response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) {
if (! $exception instanceof RequestException || $exception->response->status() !== 401) {
return false;
}
 
$request->withToken($this->getNewToken());
 
return true;
})->post(/* ... */);

Если все запросы завершаются неудачей, будет сгенерировано исключение Illuminate\Http\Client\RequestException. Если вы хотите отключить это поведение, вы можете предоставить аргумент throw со значением false. При отключении последний ответ, полученный клиентом, будет возвращен после всех попыток повтора:

$response = Http::retry(3, 100, throw: false)->post(/* ... */);

Внимание Если все запросы неудачны из-за проблем с соединением, все равно будет сгенерировано исключение Illuminate\Http\Client\ConnectionException, даже если аргумент throw установлен в false.

Обработка Ошибок

В отличие от поведения по умолчанию Guzzle, оболочка HTTP-клиента Laravel не генерирует исключения для ошибок клиента или сервера (ответы с уровнями 400 и 500 от серверов). Вы можете определить, была ли возвращена одна из этих ошибок, используя методы successful, clientError или serverError:

// Определить, если код состояния >= 200 и < 300... $response->successful();

// Определить, если код состояния >= 400... $response->failed();

// Определить, если ответ имеет код состояния 400-го уровня... $response->clientError();

// Определить, если ответ имеет код состояния 500-го уровня... $response->serverError();

// Сразу выполнить данный обратный вызов, если произошла ошибка клиента или сервера... $response->onError(callable $callback);

Бросание Исключений

Если у вас есть экземпляр ответа и вы хотите бросить экземпляр Illuminate\Http\Client\RequestException, если код состояния ответа указывает на ошибку клиента или сервера, вы можете использовать методы throw или throwIf:

use Illuminate\Http\Client\Response;
 
$response = Http::post(/* ... */);

// Выбросить исключение, если произошла ошибка клиента или сервера... $response->throw();

// Выбросить исключение, если произошла ошибка и данное условие истинно... $response->throwIf($condition);

// Выбросить исключение, если произошла ошибка и данный замыкание разрешается как истина... $response->throwIf(fn (Response $response) => true);

// Выбросить исключение, если произошла ошибка и данное условие ложно... $response->throwUnless($condition);

// Выбросить исключение, если произошла ошибка и данный замыкание разрешается как ложь... $response->throwUnless(fn (Response $response) => false);

// Выбросить исключение, если ответ имеет определенный код состояния... $response->throwIfStatus(403);

// Выбросить исключение, если ответ не имеет определенного кода состояния... $response->throwUnlessStatus(200);

return $response['user']['id'];

Экземпляр Illuminate\Http\Client\RequestException имеет открытое свойство $response, которое позволит вам осмотреть возвращенный ответ.

Метод throw возвращает экземпляр ответа, если ошибок не произошло, что позволяет вам цеплять другие операции с методом throw:

return Http::post(/* ... */)->throw()->json();

Если вы хотите выполнить дополнительную логику перед тем, как будет сгенерировано исключение, вы можете передать замыкание методу throw. Исключение будет сгенерировано автоматически после вызова замыкания, поэтому вам не нужно повторно генерировать исключение внутри замыкания:

use Illuminate\Http\Client\Response;
use Illuminate\Http\Client\RequestException;
 
return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) {
// ...
})->json();

Промежуточное ПО Guzzle

Поскольку HTTP-клиент Laravel работает на основе Guzzle, вы можете воспользоваться промежуточным ПО Guzzle для манипуляции исходящим запросом или анализа входящего ответа. Для манипуляции исходящим запросом зарегистрируйте промежуточное ПО Guzzle с помощью метода withRequestMiddleware:

use Illuminate\Support\Facades\Http;
use Psr\Http\Message\RequestInterface;
 
$response = Http::withRequestMiddleware(
function (RequestInterface $request) {
return $request->withHeader('X-Example', 'Value');
}
)->get('http://example.com');

Точно так же вы можете проанализировать входящий HTTP-ответ, зарегистрировав промежуточное ПО с помощью метода withResponseMiddleware:

use Illuminate\Support\Facades\Http;
use Psr\Http\Message\ResponseInterface;
 
$response = Http::withResponseMiddleware(
function (ResponseInterface $response) {
$header = $response->getHeader('X-Example');
 
// ...
 
return $response;
}
)->get('http://example.com');

Глобальное Промежуточное ПО

Иногда может потребоваться зарегистрировать промежуточное ПО, которое применяется к каждому исходящему запросу и входящему ответу. Для этого вы можете использовать методы globalRequestMiddleware и globalResponseMiddleware. Обычно эти методы следует вызывать в методе boot класса AppServiceProvider вашего приложения:

use Illuminate\Support\Facades\Http;
 
Http::globalRequestMiddleware(fn ($request) => $request->withHeader(
'User-Agent', 'Example Application/1.0'
));
 
Http::globalResponseMiddleware(fn ($response) => $response->withHeader(
'X-Finished-At', now()->toDateTimeString()
));

Опции Guzzle

Вы можете указать дополнительные опции запроса Guzzle, используя метод withOptions. Метод withOptions принимает массив пар ключ / значение:

$response = Http::withOptions([
'debug' => true,
])->get('http://example.com/users');

Параллельные Запросы

Иногда вам может потребоваться выполнить несколько HTTP-запросов параллельно. Другими словами, вы хотите, чтобы несколько запросов были отправлены одновременно, а не последовательно. Это может привести к существенному улучшению производительности при взаимодействии с медленными HTTP API.

К счастью, вы можете добиться этого с использованием метода pool. Метод pool принимает замыкание, которое получает экземпляр Illuminate\Http\Client\Pool, что позволяет легко добавлять запросы в пул запросов для их отправки:

use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
 
$responses = Http::pool(fn (Pool $pool) => [
$pool->get('http://localhost/first'),
$pool->get('http://localhost/second'),
$pool->get('http://localhost/third'),
]);
 
return $responses[0]->ok() &&
$responses[1]->ok() &&
$responses[2]->ok();

Как видите, каждый экземпляр ответа может быть доступен на основе порядка добавления в пул. Если хотите, вы можете назвать запросы, используя метод as, что позволяет вам получить доступ к соответствующим ответам по имени:

use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
 
$responses = Http::pool(fn (Pool $pool) => [
$pool->as('first')->get('http://localhost/first'),
$pool->as('second')->get('http://localhost/second'),
$pool->as('third')->get('http://localhost/third'),
]);
 
return $responses['first']->ok();

Настройка Параллельных Запросов

Метод pool нельзя цеплять с другими методами HTTP-клиента, такими как методы withHeaders или middleware. Если вы хотите применить пользовательские заголовки или промежуточное ПО к запросам в пуле, вы должны настроить эти опции для каждого запроса в пуле:

use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
 
$headers = [
'X-Example' => 'example',
];
 
$responses = Http::pool(fn (Pool $pool) => [
$pool->withHeaders($headers)->get('http://laravel.test/test'),
$pool->withHeaders($headers)->get('http://laravel.test/test'),
$pool->withHeaders($headers)->get('http://laravel.test/test'),
]);

Макросы

HTTP-клиент Laravel позволяет определять "макросы", которые могут служить выразительным и лаконичным механизмом настройки общих путей запросов и заголовков при взаимодействии с сервисами в вашем приложении. Для начала вы можете определить макрос в методе boot класса App\Providers\AppServiceProvider вашего приложения:

use Illuminate\Support\Facades\Http;
 
/**
* Загрузка любых служб приложения.
*/
public function boot(): void
{
Http::macro('github', function () {
return Http::withHeaders([
'X-Example' => 'example',
])->baseUrl('https://github.com');
});
}

После того как ваш макрос будет настроен, вы можете вызывать его откуда угодно в вашем приложении, чтобы создать ожидающий запрос с указанной конфигурацией:

$response = Http::github()->get('/');

Тестирование

Многие службы Laravel предоставляют функциональность для того, чтобы легко и выразительно писать тесты, и HTTP-клиент Laravel не является исключением. Метод fake фасада Http позволяет указать HTTP-клиенту возвращать поддельные / заглушенные ответы при выполнении запросов.

Поддельные Ответы

Например, чтобы указать HTTP-клиенту возвращать пустые ответы с кодом состояния 200 для каждого запроса, вы можете вызвать метод fake без аргументов:

use Illuminate\Support\Facades\Http;
 
Http::fake();
 
$response = Http::post(/* ... */);

Подделка Конкретных URL

В качестве альтернативы вы можете передать массив методу fake. Ключи массива должны представлять шаблоны URL, которые вы хотите подделать, и их ассоциированные ответы. Символ * можно использовать в качестве универсального символа. Любые запросы, выполненные по URL, который не был подделан, будут фактически выполнены. Вы можете использовать метод Http response для создания поддельных / фейковых ответов для этих конечных точек:

Http::fake([
// Заглушить JSON-ответ для точек доступа GitHub...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
 
// Заглушить строковый ответ для точек доступа Google...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);

Если вы хотите указать запасной шаблон URL, который будет подделан для всех неподдельных URL, вы можете использовать один символ *:

Http::fake([
// Заглушить JSON-ответ для точек доступа GitHub...
'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
 
// Заглушить строковый ответ для всех остальных точек доступа...
'*' => Http::response('Hello World', 200, ['Headers']),
]);

Подделка Последовательности Ответов

Иногда вам может потребоваться указать, что один URL должен возвращать серию поддельных ответов в конкретном порядке. Вы можете добиться этого с использованием метода Http::sequence для построения ответов:

Http::fake([
// Заглушить серию ответов для точек доступа GitHub...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->pushStatus(404),
]);

Когда все ответы в последовательности ответов будут использованы, любые дополнительные запросы вызовут исключение. Если вы хотите указать ответ по умолчанию, который должен возвращаться, когда последовательность пуста, вы можете использовать метод whenEmpty:

Http::fake([
// Заглушить серию ответов для точек доступа GitHub...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->whenEmpty(Http::response()),
]);

Если вы хотите подделать последовательность ответов, но не нуждаетесь в указании конкретного шаблона URL, который должен быть подделан, вы можете использовать метод Http::fakeSequence:

Http::fakeSequence()
->push('Hello World', 200)
->whenEmpty(Http::response());

Фейковый Callback

Если вам требуется более сложная логика для определения, какие ответы возвращать для определенных конечных точек, вы можете передать замыкание методу fake. Это замыкание получит экземпляр Illuminate\Http\Client\Request и должно вернуть экземпляр ответа. Внутри вашего замыкания вы можете выполнять любую логику, необходимую для определения типа ответа:

use Illuminate\Http\Client\Request;
 
Http::fake(function (Request $request) {
return Http::response('Hello World', 200);
});

Предотвращение Лишних Запросов

Если вы хотите убедиться, что все запросы, отправленные через HTTP-клиент, подделаны в пределах вашего отдельного теста или полного набора тестов, вы можете вызвать метод preventStrayRequests. После вызова этого метода любые запросы, для которых нет соответствующего поддельного ответа, вызовут исключение вместо фактического выполнения HTTP-запроса:

use Illuminate\Support\Facades\Http;
 
Http::preventStrayRequests();
 
Http::fake([
'github.com/*' => Http::response('ok'),
]);

// Возвращается ответ "ok"... Http::get('https://github.com/laravel/framework');

// Бросается исключение... Http::get('https://laravel.com');

Инспектирование Запросов

При подделке ответов иногда может потребоваться проверить запросы, которые клиент получает, чтобы удостовериться, что ваше приложение отправляет правильные данные или заголовки. Вы можете сделать это, вызвав метод Http::assertSent после вызова Http::fake.

Метод assertSent принимает замыкание, которое получит экземпляр Illuminate\Http\Client\Request и должно вернуть логическое значение, указывающее, соответствует ли запрос вашим ожиданиям. Чтобы тест прошел, должен быть хотя бы один запрос, соответствующий данным ожиданиям:

use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
 
Http::fake();
 
Http::withHeaders([
'X-First' => 'foo',
])->post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
 
Http::assertSent(function (Request $request) {
return $request->hasHeader('X-First', 'foo') &&
$request->url() == 'http://example.com/users' &&
$request['name'] == 'Taylor' &&
$request['role'] == 'Developer';
});

Если необходимо, можно утверждать, что конкретный запрос не был отправлен с использованием метода assertNotSent:

use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
 
Http::fake();
 
Http::post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
 
Http::assertNotSent(function (Request $request) {
return $request->url() === 'http://example.com/posts';
});

Метод assertSentCount можно использовать для проверки того, сколько запросов было "отправлено" во время теста:

Http::fake();
 
Http::assertSentCount(5);

Или можно использовать метод assertNothingSent, чтобы утверждать, что запросы во время теста не отправлялись:

Http::fake();
 
Http::assertNothingSent();

Запись Запросов / Ответов

Метод recorded позволяет собрать все запросы и соответствующие ответы. Метод recorded возвращает коллекцию массивов, содержащих экземпляры Illuminate\Http\Client\Request и Illuminate\Http\Client\Response:

Http::fake([
'https://laravel.com' => Http::response(status: 500),
'https://nova.laravel.com/' => Http::response(),
]);
 
Http::get('https://laravel-docs.com');
Http::get('https://nova.laravel.com/');
 
$recorded = Http::recorded();
 
[$request, $response] = $recorded[0];

Кроме того, метод recorded принимает замыкание, которое получит экземпляр Illuminate\Http\Client\Request и Illuminate\Http\Client\Response и может использоваться для фильтрации пар запрос / ответ на основе ваших ожиданий:

use Illuminate\Http\Client\Request;
use Illuminate\Http\Client\Response;
 
Http::fake([
'https://laravel.com' => Http::response(status: 500),
'https://nova.laravel.com/' => Http::response(),
]);
 
Http::get('https://laravel.com');
Http::get('https://nova.laravel.com/');
 
$recorded = Http::recorded(function (Request $request, Response $response) {
return $request->url() !== 'https://laravel.com' &&
$response->successful();
});

События

Laravel генерирует три события во время процесса отправки HTTP-запросов. Событие RequestSending генерируется перед отправкой запроса, а событие ResponseReceived - после получения ответа на данный запрос. Событие ConnectionFailed генерируется, если не получен ответ на данный запрос.

События RequestSending и ConnectionFailed оба содержат общедоступное свойство $request, которое можно использовать для проверки экземпляра Illuminate\Http\Client\Request. Точно так же событие ResponseReceived содержит свойства $request и $response, которые можно использовать для проверки экземпляров Illuminate\Http\Client\Request и Illuminate\Http\Client\Response. Вы можете зарегистрировать слушателей событий для этого события в вашем поставщике услуг App\Providers\EventServiceProvider:

/**
* Сопоставления слушателей событий для приложения.
*
* @var array
*/
protected $listen = [
'Illuminate\Http\Client\Events\RequestSending' => [
'App\Listeners\LogRequestSending',
],
'Illuminate\Http\Client\Events\ResponseReceived' => [
'App\Listeners\LogResponseReceived',
],
'Illuminate\Http\Client\Events\ConnectionFailed' => [
'App\Listeners\LogConnectionFailed',
],
];