Была задача реализовать обновление данных товаров через апи поставщика, но у них ограничение на запрос на 50 позиций на 1 запрос и последовательными запросами отрабатывает более 3 часов т.к. нужно запросить данные около 200тысяч позиций
Нужно реализовать асинхронные запросы в апи, но с нюансом чтобы он не ожидал завершения всех запросов т.к. их очень много и выпадет ошибка от пыхи на ограничение выполения запроса в 60 секунд, я обошел это пагинацией и соответственно нужно чтобы асинх запрос тоже обновлял данные по мере поступления ответов от апи
<?php
namespace tms\api;
use Yii;
use yii\base\Exception;
use yii\helpers\ArrayHelper;
use GuzzleHttp\Client;
class SimalandApi
{
/**
* @var string Базовый URL для запросов
*/
public $baseUrl = 'url';
/**
* @var string Токен для авторизации в API.
*/
private $token = 'token'; // Укажите ваш токен здесь
/**
* @var array Соответствие между ID поставщика и ID склада.
*/
private $warehouseId = [
8 => 2, // Sima-Land
21 => 1, // Sima-Land M
];
/**
* @var int Ограничение количества товаров, получаемых за один запрос к API.
*/
public const FETCH_ITEMS_LIMIT = 50;
/**
* Выполняет запрос к API для получения данных товаров по их SID.
*
* @param array $sids Массив идентификаторов SID товаров.
* @return array Массив данных товаров.
* @throws Exception Если произошла ошибка при запросе к API.
*/
public function fetchProductsBySids(array $sids): array
{
$client = new Client();
try {
$response = $client->request('GET', $this->baseUrl, [
'query' => [
'sid' => implode(',', $sids), // Преобразуем массив sids в строку для запроса
'expand' => 'stocks', // Получаем также информацию о запасах
],
'headers' => [
'Authorization' => $this->token,
'Accept' => 'application/json',
],
]);
if ($response->getStatusCode() === 200) {
$body = $response->getBody();
return json_decode($body, true);
} else {
Yii::error('Непредвиденный статус ответа ', $response->getStatusCode());
}
} catch (\Exception $e) {
Yii::error('Ошибка запроса к API: ' . $e->getMessage());
throw new Exception("Ошибка запроса к API: " . $e->getMessage());
}
return [];
}
/**
* Подготавливает данные о товарах для обновления запасов.
*
* @param int $supplierId Идентификатор поставщика.
* @param array $productItems Массив данных товаров.
* @return array Массив подготовленных данных о товарах.
*/
public function prepareProductDataForStock(int $supplierId, array $productItems): array
{
$result = [];
$warehouseId = $this->warehouseId[$supplierId];
foreach ($productItems as $productItem) {
$stocks = ArrayHelper::index($productItem['stocks'], 'stock_id');
$stock = 0;
if ($stocks[$warehouseId]) {
// Может отсутствовать balance. Вместо него приходит balance_text со значением "достаточно".
// В таком случае устанавливается остаток 777
$stock = isset($stocks[$warehouseId]['balance']) ? $stocks[$warehouseId]['balance'] : 777;
}
$result[$productItem['sid']] = [
'price' => $productItem['price'],
// 'min_quantity' => $productItem['min_qty'], // TODO Кажется, что min_quantity самое подходящее поле
'stock' => $stock,
];
}
return $result;
}
}