wmtm/storage-api-sdk

Laravel SDK for Storage API integration

Maintainers

Package info

bitbucket.org/wmtm/storage-api-sdk

pkg:composer/wmtm/storage-api-sdk

Transparency log

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

2.1.1 2026-08-25 13:31 UTC

This package is auto-updated.

Last update: 2026-08-25 13:32:48 UTC


README

Цей пакет надає зручний та потужний інструмент (SDK) для інтеграції з Storage API у Laravel-додатках. Він побудований з використанням ресурсно-орієнтованої архітектури (Resource-Oriented Architecture), підтримує fluent-білдери запитів, DTO та асинхронні пули запитів.

Встановлення

Встановіть пакет за допомогою Composer:

composer require wmtm/storage-api-sdk

Опублікуйте конфігураційний файл:

php artisan vendor:publish --tag="storage-api-config"

Конфігурація

Після публікації конфігурації у вас з'явиться файл config/storage-api.php. Ви можете налаштувати базовий URL, токен авторизації та інші параметри, використовуючи .env:

STORAGE_API_BASE_URL=https://api.example.com
STORAGE_API_TOKEN=your_oauth2_token_here
STORAGE_API_LOCALE=pl

Використання

Пакет надає фасад StorageApi, який дає доступ до різних ресурсів API.

Ресурси API

Доступні наступні ресурси:

  • products() — робота з конкретними товарами.
  • catalog() — пошук та отримання товарів з каталогу.
  • categories() — робота з категоріями та підкатегоріями.
  • stock() — перевірка кількостей товарів в каталозі та черги оновлень.

Робота з Каталогом (CatalogQueryBuilder)

Для зручного пошуку та фільтрації по каталогу реалізовано Fluent Builder. Він дозволяє формувати складні запити в читабельному вигляді.

use StorageApi\Facades\StorageApi;

$results = StorageApi::catalog()->query()
    // Основні параметри
    ->inCategory(['3', '5']) // Масив або рядок через кому
    ->search('iphone 15')
    ->priceBetween(from: 20000, to: 45000)
    
    // Пагінація
    ->paginate(page: 2, perPage: 50) 
    
    // Сортування (через хелпери або класичним методом)
    ->sortByPriceDesc() 
    // доступні: sortByPriceAsc(), sortByPriceDesc(), sortByPopularity(), 
    // sortByRelevance(), sortByUpdateTime(), sortByStartTime()
    // класичний спосіб: ->sortBy('-price')
    
    // Фільтрація
    ->withFilters(['11323_1', 'offerHas_1'])
    ->exclude(categories: [123, 456], sellers: ['bad_seller_id'])
    
    // Вузькоспеціалізовані налаштування (Solr фасети, фід-мод, фічі)
    ->facets(categoryChildren: true, allFilters: true)
    ->advancedSearch(operator: 'and', mmPercentage: 80)
    ->feedMode(isFeed: true)
    ->withFeatures(['product_properties', 'product_deliveries', 'product_text']) 
    // Доступні фічі:
    // - product_properties (характеристики товару)
    // - product_deliveries (інформація про доставку)
    // - product_text (HTML-опис товару)
    
    // Виконання запиту
    ->get(); // Повертає об'єкт CatalogResponse

Мапінг товарів та DTO (CatalogResponse)

Каталог завжди повертає об'єкт StorageApi\DTO\CatalogResponse. Цей об'єкт містить основну інформацію без зайвої вкладеності (наприклад, $response->totalCount, $response->items).

Ви також можете передати власний клас для парсингу самих товарів у масиві items. Для цього реалізуйте інтерфейс StorageApi\Contracts\CatalogProductMapper:

use StorageApi\Contracts\CatalogProductMapper;
use StorageApi\Builders\CatalogQueryBuilder;

class MyProductDto { ... }

class MyProductMapper implements CatalogProductMapper {
    public function mapProduct(array $product, ?CatalogQueryBuilder $builder = null): MyProductDto {
        return new MyProductDto($product);
    }
}

$response = StorageApi::catalog()->query()
    ->search('iphone 15')
    ->withProductMapper(new MyProductMapper()) // Передаємо мапер
    ->get();

$myDtos = $response->items; // Тепер це масив об'єктів MyProductDto

Налаштування за замовчуванням (Presets)

Для гнучкого та зручного управління налаштуваннями за замовчуванням (наприклад, дефолтна пагінація, сортування тощо) білдер підтримує пресети. Ви можете застосовувати їх як локально для запиту, так і глобально для всього додатку.

Локальне застосування:

Ви можете реалізувати інтерфейс CatalogQueryPreset або використовувати замикання (Closure).

use StorageApi\Contracts\CatalogQueryPreset;
use StorageApi\Builders\CatalogQueryBuilder;

class DefaultSitePreset implements CatalogQueryPreset
{
    public function apply(CatalogQueryBuilder $builder): void
    {
        $builder->sortByRelevance()
                ->paginate(1, 20)
                ->feedMode(false);
    }
}

// Застосування через клас
StorageApi::catalog()->query()
    ->preset(new DefaultSitePreset())
    ->search('iphone')
    ->get();
    
// Застосування через замикання
StorageApi::catalog()->query()
    ->preset(fn($b) => $b->inCategory('default_cat_id'))
    ->get();

Глобальне застосування (наприклад, у ServiceProvider):

Якщо ви хочете задати базові налаштування в одному місці (наприклад, у методі boot вашого провайдера), використовуйте addGlobalPreset(). Ці налаштування автоматично застосовуватимуться до кожного нового запиту каталогу.

use StorageApi\Builders\CatalogQueryBuilder;

public function boot(): void
{
    CatalogQueryBuilder::addGlobalPreset(new DefaultSitePreset());
    
    // Або через замикання
    CatalogQueryBuilder::addGlobalPreset(function (CatalogQueryBuilder $builder) {
        $builder->paginate(1, 40);
    }, 'pagination_preset');
}

Також білдер підтримує трейти Conditionable (when(), unless()) та Macroable з Laravel. Ви можете легко розширити білдер власними шорткатами:

// Реєстрація макроса
CatalogQueryBuilder::macro('onlyInStock', function () {
    return $this->withFilters(['in_stock' => true]);
});

// Використання
StorageApi::catalog()->query()->onlyInStock()->get();

Робота з Товарами

Отримання даних про конкретний товар:

$product = StorageApi::products()->get('15230115011');

За замовчуванням метод повертає DTO StorageApi\DTO\Product. Ви також можете передати власний мапер (ProductMapper) для автоматичного перетворення даних товару у ваш кастомний DTO:

use StorageApi\Contracts\ProductMapper;

class MyProductMapper implements ProductMapper {
    public function mapProduct(array $product): MyProductDto {
        return new MyProductDto($product);
    }
}

$productDto = StorageApi::products()->get('15230115011', new MyProductMapper());

// Для асинхронних запитів:
StorageApi::products()->getAsync('15230115011', 'my_product', new MyProductMapper());

Робота з Категоріями (повертає DTO)

Для зручності роботи з категоріями, методи повертають спеціальні DTO класи (StorageApi\DTO\Category), а не звичайні масиви. Це забезпечує типізацію та автодоповнення в IDE.

// Отримання дерева категорій
$categories = StorageApi::categories()->get('317958');

echo $categories[0]->name;
if (!empty($categories[0]->children)) {
    echo $categories[0]->children[0]->id;
}

Перевірка залишків та оновлень (Stock & Availability)

Ці ендпоінти також використовують DTO для форматування відповідей (CatalogAvailability та ProductAvailability).

// Перевірка готовності каталогу
$catalogStock = StorageApi::stock()->check(['category_id' => '123']);
if ($catalogStock->totalCount > 0) {
    echo "Знайдено {$catalogStock->totalCount} товарів.";
}

// Статус оновлення конкретного товару
$productStatus = StorageApi::stock()->queueUpdate('15230115011');
if ($productStatus->status === 'recently_updated') {
     echo "Останнє оновлення: {$productStatus->updatedAt}";
}

Асинхронні запити (Async Pool)

SDK підтримує паралельне виконання запитів за допомогою Guzzle Promises. Замість того, щоб чекати кожен запит окремо, ви можете відправити їх в пул, а потім виконати одночасно.

// 1. Додаємо запити в пул, використовуючи методи з суфіксом `Async`
// Другим аргументом передаємо ключ для подальшої ідентифікації результату
StorageApi::products()->getAsync('12345', 'my_product');
StorageApi::catalog()->query()->search('macbook')->getAsync('search_results');
StorageApi::stock()->checkAsync([], 'stock_status');

// 2. Виконуємо всі запити паралельно
$responses = StorageApi::getAsyncResponses();

// 3. Звертаємось до результатів за ключами
$product = $responses['my_product']; // Об'єкт StorageApi\DTO\Product
$catalog = $responses['search_results']; // Об'єкт StorageApi\DTO\CatalogResponse
$stock = $responses['stock_status']; // Об'єкт StorageApi\DTO\CatalogAvailability

Автоматичний мапінг в DTO працює як для синхронних, так і для асинхронних запитів.

Приклад правильного використання у джобі (Background Jobs)

Якщо ви робите серію асинхронних запитів у фоновому джобі в безкінечному циклі (або обходите великий каталог), обов'язково викликайте flushAsync() в кінці кожної ітерації (батчу), щоб уникнути витоку пам'яті:

use StorageApi\Facades\StorageApi;

while ($shouldContinue) {
    // 1. Формуємо пул з запитів
    foreach ($pages as $page) {
        StorageApi::catalog()->query()
               ->inCategory($categoryId)
               ->paginate($page)
               ->getAsync("page_{$page}");
    }

    // 2. Виконуємо запити
    $responses = StorageApi::getAsyncResponses();
    
    // 3. Обробляємо результати
    // ...

    // 4. Очищуємо пул та накопичені результати
    StorageApi::flushAsync();
}

Розширені можливості (Advanced)

SDK надає додаткові інструменти для тонкого налаштування Guzzle-клієнта, що дозволяє легше інтегруватися в існуючі проєкти з їхньою специфічною бізнес-логікою.

1. Кастомні правила повторних запитів (Retry Policies)

За замовчуванням SDK автоматично повторює запити лише при помилках 500+, 429 та обривах з'єднання. Ви можете додати свою логіку (наприклад, щоб повторювати запит при 404):

StorageApi::retryWhen(function ($retries, $request, $response, $exception) {
    if ($exception && $exception->getCode() === 404) {
        return true; // Повторювати запит при 404
    }
    return null; // Повернути null, щоб передати рішення стандартному механізму SDK
});

2. Interceptors / Middleware (Логування)

Якщо вам потрібно логувати запити (або їх час виконання) через існуючі інструменти (як DebugParserRequest), ви можете додати власні Guzzle Middleware:

use GuzzleHttp\Middleware;

StorageApi::pushMiddleware(Middleware::tap(
    function ($request, $options) {
        logger('Відправляємо запит', ['url' => $request->getUri()]);
    }
), 'my_logger');

3. Аліаси параметрів запиту (Query Aliases)

Якщо у вашому проєкті використовуються специфічні ключі для фільтрів (наприклад, category.id замість categoryId), ви можете вказати білдеру автоматично їх перейменовувати при передачі сирого масиву:

use StorageApi\Builders\CatalogQueryBuilder;

// Рекомендуємо додавати це в глобальний пресет (наприклад, в AppServiceProvider)
CatalogQueryBuilder::addGlobalPreset(function (CatalogQueryBuilder $builder) {
    $builder->withQueryAliases([
        'category.id' => 'categoryId',
        'phrase' => 'search',
    ]);
});

// Тепер ви можете передати старий масив, і білдер автоматично переведе ключі у правильні:
StorageApi::catalog()->query()
    ->withParameters(['category.id' => 123, 'phrase' => 'iphone'])
    ->get(); // Під капотом відправить ?categoryId=123&search=iphone

4. Обробка архівованих товарів

Якщо API повертає 404 з тілом {"status": "ARCHIVED"}, SDK автоматично викине виняток StorageApi\Exceptions\ProductArchivedException. Це дозволяє вашій бізнес-логіці відрізняти "товар тимчасово не знайдено" від "товар назавжди знято з публікації".

Вимоги

  • PHP 8.1+
  • Laravel 10.0+
  • GuzzleHttp 7.8+