Mindbox - это российская экосистема для email-, sms-, push-рассылок с персонализацией маркетинга и цен.
Эта библиотека предоставляет удобные классы для быстрой интеграции с API-методами Mindbox на PHP. А продуманные интерфейсы помогут быстро расширить функциональность библиотеки.
composer require antonowano/mindboxБиблиотека зависит только от интерфейсов PSR. Для работы нужны реализации:
- PSR-18 / PSR-17 - HTTP-клиент и фабрики запросов/стримов (например, Guzzle:
composer require guzzlehttp/guzzle); - PSR-3 - логгер, если используете
SilentApiClient(например, Monolog:composer require monolog/monolog).
SilentApiClient оборачивает обычный клиент, ловит исключения и пишет их в лог вместо проброса наружу.
Для работы клиента нужны:
| Параметр | Описание |
|---|---|
endpointId |
Идентификатор эндпоинта в Mindbox |
secretKey |
Секретный ключ API |
defaultPrefix |
Префикс операций по умолчанию (например, Website.) |
deviceUuid |
UUID устройства для операций с needDeviceUuid: true |
В боевом приложении deviceUuid берётся из cookie mindboxDeviceUUID
(его выставляет JS SDK Mindbox).
$data = $apiClient->sync(new DefaultJsonOperation(
name: '{prefix}GetProfile',
needDeviceUuid: false,
requestData: [
'customer' => [
'ids' => [
'webID' => 3165145,
],
],
],
));$data = $apiClient->async(new DefaultJsonOperation(
name: '{prefix}ViewProduct',
needDeviceUuid: true,
requestData: [
'viewProduct' => [
'productGroup' => [
'ids' => [
'brandProducts' => 3914381,
],
],
],
],
));$csvFile = tmpfile();
fputcsv($csvFile, [
'ExternalIdentityWebID',
'IsSubscribedByEmail',
'IsSubscribedByEmailOnService',
'IsSubscribedByEmailOnTrigger',
'IsSubscribedByEmailOnLoyalty',
], ';', '"', '\\');
fputcsv($csvFile, [
'3165145',
'0',
'1',
'1',
'1',
], ';', '"', '\\');
rewind($csvFile);
$data = $apiClient->bulk(
new DefaultBulkOperation(
name: 'DirectCrm.Customers.Edit',
resource: $csvFile,
),
new MindboxTransactionId()
);Эти же примеры с инициализацией можно найти в папке scripts/.
Для более элегантной работы с библиотекой лучше реализовывать собственные классы
вместо DefaultIntegration, DefaultJsonOperation и других дефолтных реализаций.
Интерфейсы Integration, AsyncOperation, SyncOperation и BulkOperation
как раз для этого и предусмотрены.
Вместо DefaultIntegration можно описать своё поведение -
например, читать deviceUuid из cookie:
use Antonowano\Mindbox\Integration;
readonly class CustomIntegration implements Integration
{
public function endpointId(): string
{
return env('MINDBOX_ENDPOINT_ID');
}
public function secretKey(): string
{
return env('MINDBOX_SECRET_KEY');
}
public function defaultPrefix(): string
{
return env('MINDBOX_DEFAULT_PREFIX');
}
public function deviceUuid(): ?string
{
return $_COOKIE['mindboxDeviceUUID'] ?? null;
}
}Вместо передачи массива в DefaultJsonOperation можно инкапсулировать операцию
в отдельный класс:
use Antonowano\Mindbox\AsyncOperation;
readonly class ViewProduct implements AsyncOperation
{
public function __construct(
private int $productId,
) {
}
public function name(string $defaultPrefix): string
{
return $defaultPrefix . 'ViewProduct';
}
public function needDeviceUuid(): bool
{
return true;
}
public function requestData(): array
{
return [
'viewProduct' => [
'productGroup' => [
'ids' => [
'brandProducts' => $this->productId,
],
],
],
];
}
}Использование:
$data = $apiClient->async(new ViewProduct($productId));Такой подход делает вызовы API читаемее: в код передаются доменные данные, а детали формирования запроса остаются в классе операции.
composer require antonowano/mindboxОтдельно ставить Guzzle и логгер не нужно: в Laravel уже есть Guzzle (через фреймворк) и PSR-3 логгер (Log).
Добавьте в .env:
MINDBOX_ENDPOINT_ID=company_endpoint_web
MINDBOX_SECRET_KEY=secret_key
MINDBOX_DEFAULT_PREFIX=Website.Добавьте секцию mindbox в config/services.php:
'mindbox' => [
'endpoint_id' => env('MINDBOX_ENDPOINT_ID'),
'secret_key' => env('MINDBOX_SECRET_KEY'),
'default_prefix' => env('MINDBOX_DEFAULT_PREFIX'),
],После изменения .env в production не забудьте пересобрать кэш конфигурации:
php artisan config:cacheВ коде значения читаются через config('services.mindbox.*'), а не через env() напрямую.
В AppServiceProvider (или отдельном сервисе-провайдере):
use Antonowano\Mindbox\ApiClient;
use Antonowano\Mindbox\DefaultIntegration;
use Antonowano\Mindbox\Integration;
use Antonowano\Mindbox\MindboxApiClient;
use Antonowano\Mindbox\MindboxEndpointUrl;
use Antonowano\Mindbox\SilentApiClient;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\HttpFactory;
use Illuminate\Support\Facades\Log;
public function register(): void
{
$this->app->singleton(Integration::class, function () {
return new DefaultIntegration(
endpointId: config('services.mindbox.endpoint_id'),
secretKey: config('services.mindbox.secret_key'),
defaultPrefix: config('services.mindbox.default_prefix'),
deviceUuid: $_COOKIE['mindboxDeviceUUID'] ?? null,
);
});
$this->app->singleton(ApiClient::class, function ($app) {
$integration = $app->make(Integration::class);
$httpFactory = new HttpFactory();
$client = new MindboxApiClient(
integration: $integration,
endpointUrl: new MindboxEndpointUrl($integration),
httpClient: new Client(),
requestFactory: $httpFactory,
streamFactory: $httpFactory,
);
return $client;
// или, если никогда не хотите обрабатывать Exception, тогда так:
// return new SilentApiClient($client, Log::channel());
});
}Если вы не хотите обрабатывать исключения с помощью try..catch,
тогда оберните зарегистрированный клиент классом SilentApiClient.
Все Exception будут направлены в логгер, а само приложение не упадёт.
public function product(int $productId, ApiClient $client) {
$client = new SilentApiClient($client, Log::channel());
$client->async(new ViewProduct($productId));
}