diff --git a/mirzaev/skillparts/system/controllers/ProductController.php b/mirzaev/skillparts/system/controllers/ProductController.php index 932596d..320a3c2 100644 --- a/mirzaev/skillparts/system/controllers/ProductController.php +++ b/mirzaev/skillparts/system/controllers/ProductController.php @@ -95,12 +95,7 @@ class ProductController extends Controller // Не существует товар к которому планируется соединение // Инициализация товара - $to = new Product(); - - // Запись артикула - $to->catn = (string) $target; - - if ($to->save()) { + if ($to = Product::writeEmpty((string) $target)) { // Удалось записать товар } else { // Не удалось записать товар @@ -116,19 +111,8 @@ class ProductController extends Controller } } - // Запись ребра - if ($edge = ProductEdgeProduct::writeSafe(Product::collectionName() . "/$from->_key", Product::collectionName() . "/$to->_key", data: ['type' => 'analogue'])) { - // Ребро сохранено - - // Запись в журнал о соединении - $from->journal('connect', [ - 'to' => Product::collectionName() . "/$to->_key" - ]); - - // Запись в журнал о соединении - $to->journal('connect', [ - 'from' => Product::collectionName() . "/$from->_key" - ]); + if (($edge = $from->synchronization($to)) instanceof ProductEdgeProduct) { + // Ребро создано // Запись в буфер возврата $return['alert'] = "Продукты успешно соединены ребром: $edge->_key"; @@ -202,37 +186,9 @@ class ProductController extends Controller if ($to = Product::searchByCatn($target)) { // Существует товар который нужно отсоединить - // Поиск ребра - if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$from->_key", Product::collectionName() . "/$to->_key", type: 'analogue')[0]) { - // Найдено ребро (from: $from, to: $to) - } else if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$from->_key", type: 'analogue')[0]) { - // Найдено ребро (from: $to, to: $from) - } else { - // Не найдены ребра - - // Запись кода ответа - yii::$app->response->statusCode = 500; - - // Запись в буфер возврата - $return['alert'] = "Не удалось найти связь с этим товаром: $target"; - - // Переход в конец алгоритма - goto end; - } - - if ($edge->delete() > 0) { + if ($from->disconnect($to)) { // Удалось удалить ребро (связь) - // Запись в журнал о разъединении - $from->journal('disconnect', [ - 'to' => Product::collectionName() . "/$to->_key" - ]); - - // Запись в журнал о соединении - $to->journal('disconnect', [ - 'from' => Product::collectionName() . "/$from->_key" - ]); - // Запись в буфер возврата $return['alert'] = "Продукты успешно отсоединены"; } else { @@ -243,7 +199,7 @@ class ProductController extends Controller yii::$app->response->statusCode = 500; // Запись в буфер возврата - $return['alert'] = "Не удалось удалить ребро между $catn и $target"; + $return['alert'] = "Не удалось отсоединить $target от $catn"; // Переход в конец алгоритма goto end; diff --git a/mirzaev/skillparts/system/controllers/SearchController.php b/mirzaev/skillparts/system/controllers/SearchController.php index 6a24fd5..191f711 100644 --- a/mirzaev/skillparts/system/controllers/SearchController.php +++ b/mirzaev/skillparts/system/controllers/SearchController.php @@ -8,29 +8,15 @@ use yii; use yii\web\Controller; use yii\web\Response; -use app\models\Account; use app\models\Product; -use app\models\Supply; use app\models\Search; -use app\models\connection\Dellin; -use app\models\Settings; - -use Exception; - +/** + * @todo + * 1. Ограничение доступа + */ class SearchController extends Controller { - - - - - - - - - - - /** * @todo * 1. Сессию привязать к аккаунту и проверку по нему делать, иначе её можно просто сбрасывать @@ -161,222 +147,26 @@ class SearchController extends Controller ])) { // Данные найдены по поиску в полях Каталожного номера - foreach ($response as &$row) { - // Перебор продуктов + // Генерация данных для представления + $response = Search::content(products: $response); - // Поиск поставок привязанных к продуктам - $connections = Supply::searchByEdge( - from: 'product', - to: 'supply', - edge: 'supply_edge_product', - limit: 11, - direction: 'OUTBOUND', - subquery_where: [ - ['product._key' => $row['_key']], - ['supply.catn == product.catn'], - ['supply_edge_product.type' => 'connect'] - ], - where: 'supply._id == supply_edge_product[0]._from', - select: '{supply, supply_edge_product}' - ); - - // Инициализация буфера - $buffer_connections = []; - - if (count($connections) === 11) { - // Если в базе данных хранится много поставок - - // Инициализация - $row['overload'] = true; - } - - foreach ($connections as $key => &$connection) { - // Перебор поставок - - if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 - || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 - ) { - // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы - - // Скрыть из выдачи - unset($connections[$key]); - - continue; - } - - // Инициализация аккаунта - $connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']); - - // Инициализация продукта - $connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']); - - try { - // Инициализация данных геолокации - - try { - $from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; - } catch (Exception $e) { - $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; - } - - try { - $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36; - } catch (Exception $e) { - $to = 36; - } - - if ( - ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false) - && time() < $buffer_connection['expires'] - ) { - // Найдены данные доставки в буфере - // и срок хранения не превышен, информация актуальна - - // Запись в буфер вывода - $connection['delivery'] = $buffer_connection['data']; - $connection['delivery']['type'] = 'auto'; - } else { - // Инициализация инстанции продукта в базе данных - $product = Product::searchByCatn($connection['product']['catn']); - - // Инициализация доставки Dellin (автоматическая) - $product->bffr = [ - "$from-$to" => [ - 'data' => $connection['delivery'] = Dellin::calcDeliveryAdvanced( - $from, - $to, - (int) ($connection['product']['wght'] ?? 0), - (int) ($connection['product']['dmns']['x'] ?? 0), - (int) ($connection['product']['dmns']['y'] ?? 0), - (int) ($connection['product']['dmns']['z'] ?? 0) - ), - 'expires' => time() + 86400 - ] - ] + ($product->bffr ?? []); - $connection['delivery']['type'] = 'auto'; - - // Отправка в базу данных - $product->update(); - } - } catch (Exception $e) { - $connection['delivery']['error'] = true; - - // var_dump($e->getMessage()); - // var_dump($e->getTrace()); - // var_dump($e->getFile()); - - // var_dump(json_decode($e->getMessage(), true)['errors']); - // die; - } - - // Инициализация цены (цена поставки + цена доставки + наша наценка) - $connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); - - // Инициализация версии для рассчета доставки по воздуху - $buffer_delivery_avia = $connection; - - try { - // Инициализация данных геолокации - - if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 - || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 - ) { - // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы - // Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть - // Однако я это оставлю для возможных доработок в будущем - - // Скрыть из выдачи - unset($connections[$key]); - - continue; - } - - try { - $from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; - } catch (Exception $e) { - $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; - } - - try { - $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36; - } catch (Exception $e) { - $to = 36; - } - - if ( - ($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false) - && time() < $buffer_connection['expires'] - ) { - // Найдены данные доставки в буфере - // и срок хранения не превышен, информация актуальна - - // Запись в буфер вывода - $buffer_delivery_avia['delivery'] = $buffer_connection['data']; - $buffer_delivery_avia['delivery']['type'] = 'avia'; - } else { - // Инициализация инстанции продукта в базе данных - $product = Product::searchByCatn($buffer_delivery_avia['product']['catn']); - - // Инициализация доставки Dellin (автоматическая) - $product->bffr = [ - "$from-$to-avia" => [ - 'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced( - $from, - $to, - (int) ($buffer_delivery_avia['product']['wght'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0), - (int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0), - avia: true - ), - 'expires' => time() + 86400 - ] - ] + ($product->bffr ?? []); - $buffer_delivery_avia['delivery']['type'] = 'avia'; - - // Отправка в базу данных - $product->update(); - } - } catch (Exception $e) { - $buffer_delivery_avia['delivery']['error'] = true; - - // echo '
'; - // var_dump($e->getMessage()); - // var_dump($e->getTrace()); - // var_dump($e->getFile()); - - // var_dump(json_decode($e->getMessage(), true)['errors']); - // die; - } - - if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) { - // Если рассчиталась доставка самолётом - - // Инициализация цены (цена поставки + цена доставки + наша наценка) - $buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); - - // Запись в буфер - $buffer_connections[] = $buffer_delivery_avia; - } - } - - // Запись обработанных данных - $row['supplies'] = array_merge($connections, $buffer_connections); - } - - // Запись ответа - $return = [ - 'panel' => $this->renderPartial('/search/panel', compact('response')), - '_csrf' => yii::$app->request->getCsrfToken() - ]; - - if ((int) yii::$app->request->post('advanced')) { - // Полноценный поиск + if (yii::$app->request->isPost) { + // POST-запрос // Запись ответа - $return['main'] = $this->renderPartial('/search/index', compact('response')); - $return['hide'] = 1; - $return['redirect'] = '/search?type=product&q=' . $query; + $return = [ + 'panel' => $this->renderPartial('/search/panel', compact('response', 'query')), + '_csrf' => yii::$app->request->getCsrfToken() + ]; + + if ((int) yii::$app->request->post('advanced')) { + // Полноценный поиск + + // Запись ответа + $return['main'] = $this->renderPartial('/search/index', compact('response', 'query')); + $return['hide'] = 1; + $return['redirect'] = '/search?type=product&q=' . $query; + } } } else { // Данные не найдены @@ -413,7 +203,7 @@ class SearchController extends Controller $advanced = true; - return $this->render('/search/index', compact('response', 'timer', 'advanced')); + return $this->render('/search/index', compact('response', 'timer', 'advanced', 'query')); } } diff --git a/mirzaev/skillparts/system/models/Edge.php b/mirzaev/skillparts/system/models/Edge.php index 070a90d..7f4584a 100644 --- a/mirzaev/skillparts/system/models/Edge.php +++ b/mirzaev/skillparts/system/models/Edge.php @@ -112,7 +112,7 @@ abstract class Edge extends Document * @todo * 1. Удалить $type и оставить только $data */ - public static function write(string $_from, string $_to, string $type, array $data = []): ?static + public static function write(string $_from, string $_to, string $type = '', array $data = []): ?static { // Инициализация $edge = new static; @@ -159,19 +159,31 @@ abstract class Edge extends Document } /** - * Поиск рёбер + * Поиск рёбер по направлению */ public static function search(string $target, string $direction = 'OUTBOUND', string $type = '', int $limit = 1): static|array|null { - if ($direction === 'OUTBOUND') { - $query = self::find()->where(['_from' => $target, 'type' => $type]); - } else if ($direction === 'INBOUND') { - $query = self::find()->where(['_to' => $target, 'type' => $type]); + if (str_contains($direction, 'OUTBOUND')) { + // Исходящие рёбра + + $query = static::find()->where(['_from' => $target, 'type' => $type]); + } else if (str_contains($direction, 'INBOUND')) { + // Входящие рёбра + + $query = static::find()->where(['_to' => $target, 'type' => $type]); + } else if (str_contains($direction, 'ANY')) { + // Исходящие и входящие рёбра + + return static::search(target: $target, direction: 'OUTBOUND', type: $type, limit: $limit) + static::search(target: $target, direction: 'INBOUND', type: $type, limit: $limit); } if ($limit < 2) { + // Одно ребро или меньше + return $query->one(); } else { + // Несколько рёбер + return $query->limit($limit)->all(); } } diff --git a/mirzaev/skillparts/system/models/Product.php b/mirzaev/skillparts/system/models/Product.php index 36d5de7..56d152a 100644 --- a/mirzaev/skillparts/system/models/Product.php +++ b/mirzaev/skillparts/system/models/Product.php @@ -16,6 +16,8 @@ use DateTime; use DateTimeZone; use Exception; +use phpDocumentor\Reflection\DocBlock\Tags\Var_; +use stdClass; /** * Продукт (в ассортименте магазина) @@ -596,10 +598,183 @@ class Product extends Document */ public static function searchAnalogs(string $catn, int $limit = 30): ?array { + // Инициализация буфера возврата + $return = []; + // Поиск ключей аналогов $analogs = ProductEdgeProduct::searchConnections(self::searchByCatn($catn)->_key, $limit); + foreach ($analogs as $analog) { + // Перебор найденных ключей (_key) аналогов - return []; + if ($analog = Product::searchById(self::collectionName() . "/$analog")) { + // Найден товар + + // Запись в буфер вывода + $return[] = $analog; + } else { + // Не найден товар + } + } + + return $return; + } + + /** + * Синхронизация аналогов + * + * Связывает с товаром и связанными с ним товарами в одну группу + * + * @param self $to Цель + * + * @return array Созданные рёбра + */ + public function synchronization(self $to): array + { + // Инициализация буфера сохранённых рёбер + $edges = []; + + // Инициализация списка товаров в группе и буфера для связей "всех со всеми" + $products = $_products = array_unique(array_merge($this->connections(), $to->connections(), [Product::collectionName() . "/$to->_key"])); + + foreach ($products as $product) { + // Перебор связей для создания связей во всех товарах + + // Удаление обрабатываемого товара из буферного списка товаров + unset($_products[array_search($product, $_products)]); + + foreach ($_products as $_product) { + // Перебор товаров из буфера + + if ($from = self::searchById($product)) { + // Товар найден + } else { + if ($from = Product::writeEmpty($product)) { + // Удалось записать товар + } else { + // Не удалось записать товар + + continue; + } + } + + if ($to = self::searchById($_product)) { + // Товар найден + } else { + if ($to = Product::writeEmpty($_product)) { + // Удалось записать товар + } else { + // Не удалось записать товар + + continue; + } + } + + if ($edge = $from->connect($to)) { + // Ребро создано + + // Запись в буфер + $edges[] = $edge; + } + } + } + + return $edges; + } + + /** + * Подключение аналога + * + * СВЯЗЫВАЕТ ТОЛЬКО ДВА ТОВАРА, ГРУППА ОСТАНЕТСЯ НЕИЗМЕННОЙ + * + * @param self $to Цель + */ + public function connect(self $to): ?ProductEdgeProduct + { + if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", limit: 1)) { + // Дубликат найден + } else if (ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", limit: 1)) { + // Дубликат найден (наоборот) + + // Вероятно эта проверка здесь не нужна, так как мы знаем входные данные + } else { + // Дубликаты не найдены + + if ($edge = ProductEdgeProduct::write(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", data: ['type' => 'analogue'])) { + // Ребро сохранено + + // Запись в журнал о соединении + $this->journal('connect analogue', [ + 'to' => Product::collectionName() . "/$to->_key" + ]); + + // Запись в журнал о соединении + $to->journal('connect analogue', [ + 'from' => Product::collectionName() . "/$this->_key" + ]); + + return $edge; + } + } + + return null; + } + + /** + * Отключение аналога + * + * @param self $to Цель + */ + public function disconnect(self $to): bool + { + // Поиск ребра + if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$this->_key", Product::collectionName() . "/$to->_key", type: 'analogue')[0]) { + // Найдено ребро (from: $this, to: $to) + } else if ($edge = @ProductEdgeProduct::searchByVertex(Product::collectionName() . "/$to->_key", Product::collectionName() . "/$this->_key", type: 'analogue')[0]) { + // Найдено ребро (from: $to, to: $this) + } else { + // Не найдены ребра + + return false; + } + + if ($edge->delete() > 0) { + // Удалось удалить ребро (связь) + + // Запись в журнал о разъединении + $this->journal('disconnect analogue', [ + 'to' => Product::collectionName() . "/$to->_key" + ]); + + // Запись в журнал о соединении + $to->journal('disconnect analogue', [ + 'from' => Product::collectionName() . "/$this->_key" + ]); + + return true; + } + + return false; + } + + /** + * Найти все связанные товары + * + * @param int $limit Ограничение по максимальному значению + */ + public function connections(int $limit = 100): array + { + // Инициализация буфера связанных товаров + $products = []; + + foreach (ProductEdgeProduct::search(self::collectionName() . "/$this->_key", direction: 'ANY', type: 'analogue', limit: $limit) as $edge) { + // Перебор связей для создания списка товаров (вершин) + + // Добавление товаров (вершин) в буфер (подразумевается, что без дубликатов) + if (!in_array($edge->_from, $products, true)) $products[] = $edge->_from; + if (!in_array($edge->_to, $products, true)) $products[] = $edge->_to; + } + + return $products; } } diff --git a/mirzaev/skillparts/system/models/ProductEdgeProduct.php b/mirzaev/skillparts/system/models/ProductEdgeProduct.php index 6b77c0f..0a8b798 100644 --- a/mirzaev/skillparts/system/models/ProductEdgeProduct.php +++ b/mirzaev/skillparts/system/models/ProductEdgeProduct.php @@ -34,7 +34,7 @@ class ProductEdgeProduct extends Edge preg_match_all('/\w+\/([0-9]+)/', $edge->_to, $matches); // Запись артикула в буфер вывода - $return[] = $matches[1]; + $return[] = $matches[1][0]; } // Перерасчет ограничения по количеству @@ -47,10 +47,10 @@ class ProductEdgeProduct extends Edge // Перебор найденных рёбер // Извлечение ключа - preg_match_all('/\w+\/([0-9]+)/', $edge->_to, $matches); + preg_match_all('/\w+\/([0-9]+)/', $edge->_from, $matches); // Запись артикула в буфер вывода - $return[] = $matches[1]; + $return[] = $matches[1][0]; } return $return; diff --git a/mirzaev/skillparts/system/models/Search.php b/mirzaev/skillparts/system/models/Search.php index c1a8b57..70c104b 100644 --- a/mirzaev/skillparts/system/models/Search.php +++ b/mirzaev/skillparts/system/models/Search.php @@ -5,9 +5,14 @@ declare(strict_types=1); namespace app\models; use yii; -use yii\web\IdentityInterface; use app\models\traits\SearchByEdge; +use app\models\connection\Dellin; +use app\models\Settings; + +use datetime; +use exception; +use throwable; /** * Поиск @@ -110,4 +115,435 @@ class Search extends Document return null; } + + + /** + * Поиск содержимого поиска + * + * @todo В будущем возможно заказ не только поставок реализовать + * Переписать реестр и проверку на дубликаты, не понимаю как они работают + */ + public static function content(array $products, int $limit = 50, int $page = 1): Supply|int|array|null + { + // Инициализация буфера вывода + $response = $products; + + // Генерация сдвига по запрашиваемым данным (пагинация) + $offset = $limit * ($page - 1); + + foreach ($response as &$row) { + // Перебор продуктов + + if ($row instanceof Product) { + // В массиве объект - инстанция товара + + // Преобразование к массиву в буфер (унификация данных) + $_row = $row->attributes; + } else { + // В массиве не товар (подразумевается, что это массив с параметрами) + + // Запись в буфер (унификация данных) + $_row = $row; + } + + // Поиск поставок привязанных к продуктам + $connections = Supply::searchByEdge( + from: 'product', + to: 'supply', + edge: 'supply_edge_product', + limit: $limit, + offset: $offset, + direction: 'OUTBOUND', + subquery_where: [ + ['product._key' => $_row['_key']], + ['supply.catn == product.catn'], + ['supply_edge_product.type' => 'connect'] + ], + where: 'supply._id == supply_edge_product[0]._from', + select: '{supply, supply_edge_product}' + ); + + // Инициализация буфера + $buffer_connections = []; + + if (count($connections) === 11) { + // Если в базе данных хранится много поставок + + // Инициализация + $_row['overload'] = true; + } + + foreach ($connections as $key => &$connection) { + // Перебор поставок + + if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $connection['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 + || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 + ) { + // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы + + // Скрыть из выдачи + unset($connections[$key]); + + continue; + } + + // Инициализация аккаунта + $connection['account'] = Account::searchBySupplyId($connection['supply_edge_product'][0]['_from']); + + // Инициализация продукта + $connection['product'] = Product::searchBySupplyId($connection['supply_edge_product'][0]['_from']); + + try { + // Инициализация данных геолокации + + try { + $from = (int) $connection['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; + } catch (exception $e) { + $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; + } + + try { + $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36; + } catch (Exception $e) { + $to = 36; + } + + if ( + ($buffer_connection = $connection['product']['bffr']["$from-$to"] ?? false) + && time() < $buffer_connection['expires'] + ) { + // Найдены данные доставки в буфере + // и срок хранения не превышен, информация актуальна + + // Запись в буфер вывода + $connection['delivery'] = $buffer_connection['data']; + $connection['delivery']['type'] = 'auto'; + } else { + // Инициализация инстанции продукта в базе данных + $product = Product::searchByCatn($connection['product']['catn']); + + // Инициализация доставки Dellin (автоматическая) + $product->bffr = [ + "$from-$to" => [ + 'data' => $connection['delivery'] = Dellin::calcDeliveryAdvanced( + $from, + $to, + (int) ($connection['product']['wght'] ?? 0), + (int) ($connection['product']['dmns']['x'] ?? 0), + (int) ($connection['product']['dmns']['y'] ?? 0), + (int) ($connection['product']['dmns']['z'] ?? 0) + ), + 'expires' => time() + 86400 + ] + ] + ($product->bffr ?? []); + $connection['delivery']['type'] = 'auto'; + + // Отправка в базу данных + $product->update(); + } + } catch (Exception $e) { + $connection['delivery']['error'] = true; + + // var_dump($e->getMessage()); + // var_dump($e->getTrace()); + // var_dump($e->getFile()); + + // var_dump(json_decode($e->getMessage(), true)['errors']); + // die; + } + + // Инициализация цены (цена поставки + цена доставки + наша наценка) + $connection['cost'] = $cost + ($connection['delivery']['price']['all'] ?? $connection['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); + + // Инициализация версии для рассчета доставки по воздуху + $buffer_delivery_avia = $connection; + + try { + // Инициализация данных геолокации + + if (($cost = $cost['ЦенаЗаЕдиницу'] ?? $buffer_delivery_avia['supply_edge_product'][0]['onec']['Цены']['Цена']['ЦенаЗаЕдиницу']) < 1 + || ($amount = $connection['supply']['amnt'] ?? $connection['supply_edge_product'][0]['onec']['Количество']) < 1 + ) { + // Цена меньше единицы (подразумевается как ошибка) или количество меньше единицы + // Этот код не будет выполняться, так как цена одна на обе позиции и аналогичная проверка выше уже есть + // Однако я это оставлю для возможных доработок в будущем + + // Скрыть из выдачи + unset($connections[$key]); + + continue; + } + + try { + $from = (int) $buffer_delivery_avia['account']['opts']['delivery_from_terminal'] ?? empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; + } catch (Exception $e) { + $from = empty(Settings::search()->delivery_from_default) ? 36 : (int) Settings::search()->delivery_from_default; + } + + try { + $to = (int) yii::$app->user->identity->opts['delivery_to_terminal'] ?? 36; + } catch (Exception $e) { + $to = 36; + } + + if ( + ($buffer_connection = $buffer_delivery_avia['product']['bffr']["$from-$to-avia"] ?? false) + && time() < $buffer_connection['expires'] + ) { + // Найдены данные доставки в буфере + // и срок хранения не превышен, информация актуальна + + // Запись в буфер вывода + $buffer_delivery_avia['delivery'] = $buffer_connection['data']; + $buffer_delivery_avia['delivery']['type'] = 'avia'; + } else { + // Инициализация инстанции продукта в базе данных + $product = Product::searchByCatn($buffer_delivery_avia['product']['catn']); + + // Инициализация доставки Dellin (автоматическая) + $product->bffr = [ + "$from-$to-avia" => [ + 'data' => $buffer_delivery_avia['delivery'] = Dellin::calcDeliveryAdvanced( + $from, + $to, + (int) ($buffer_delivery_avia['product']['wght'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['x'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['y'] ?? 0), + (int) ($buffer_delivery_avia['product']['dmns']['z'] ?? 0), + avia: true + ), + 'expires' => time() + 86400 + ] + ] + ($product->bffr ?? []); + $buffer_delivery_avia['delivery']['type'] = 'avia'; + + // Отправка в базу данных + $product->update(); + } + } catch (exception $e) { + $buffer_delivery_avia['delivery']['error'] = true; + + // echo ''; + // var_dump($e->getMessage()); + // var_dump($e->getTrace()); + // var_dump($e->getFile()); + + // var_dump(json_decode($e->getMessage(), true)['errors']); + // die; + } + + if (!isset($buffer_delivery_avia['delivery']['error']) || $buffer_delivery_avia['delivery']['error'] !== true) { + // Если рассчиталась доставка самолётом + + // Инициализация цены (цена поставки + цена доставки + наша наценка) + $buffer_delivery_avia['cost'] = $cost + ($buffer_delivery_avia['delivery']['price']['all'] ?? $buffer_delivery_avia['delivery']['price']['one'] ?? 0) + ($settings['increase'] ?? 0); + + // Запись в буфер + $buffer_connections[] = $buffer_delivery_avia; + } + } + + // Запись обработанных данных + $_row['supplies'] = array_merge($connections, $buffer_connections); + + // Запись из буфера + $row = $_row; + } + + return $response; + } + + /** + * Генерация HTML-кода с найденным товаром + * + * @param array $row Товар сгенерированный через Search::content() + * @param string|null $cpny Компания (сейчас пока что это $name - Название) + * @param string|null $covr Обложка + * @param string|null $catg Категория + * @param string|null $catn Артикул + * @param array $empties Реестр не найденных товаров + * + * @return string HTML-элемент с товаром + */ + public static function generate(array &$row, string|null &$cpny = null, string|null &$covr = null, string|null &$catg = null, string|null &$catn = null, array &$empties = []): string + { + // Инициализация + extract($row); + $cpny ?? $cpny = 'Без названия'; + // $dscr ?? $dscr = 'Описание'; + $catg ?? $catg = 'Категория'; + $catn ?? $catn = 'Неизвестно'; + foreach ($imgs ?? [] as $img) { + // Перебор изображений для обложки + + if ($img['covr'] ?? false) { + // Обложка найдена + + $covr = $img['h150']; + + break; + } + } + + if (is_null($covr)) { + // Обложка не инициализирована + + if (!$covr = $imgs[0]['h150'] ?? false) { + // Не удалось использовать первое изображение как обложку + + // Запись обложки по умолчанию + $covr = '/img/covers/h150/product.png'; + } + } + + // Инициализация буфера с HTML поставок + $supplies_html = ''; + + // Инициализация блокировщика для пустого блока (на случай, если нет поставок, чтобы не было дубликатов вывода) + $empty_block = false; + + // Инициализация счётчика поставок + $supplies_amount = count($row['supplies']); + + // Инициализация указателя номера цикла + $supply_iterator = 1; + + foreach (empty($row['supplies']) || count($row['supplies']) === 0 ? [null] : $row['supplies'] as $supply) { + // Инициализация модификатора класса + if ($supplies_amount > $supply_iterator) { + // Это не последняя строка с товаром и его поставками + + $supply_class_modifier = 'mb-1'; + } else { + // Это последняя строка с товаром и его поставками + + $supply_class_modifier = ''; + } + + if (is_null($supply)) { + // Поставки отсутствуют + + // Генерация данных об отсутствии заказов + goto no_supplies; + } else { + // Обычная обработка поставки + + // Инициализация переменных + extract($supply); + } + + // Инициализация цены + $price_raw = $cost; + $price = $price_raw . ' ' . $supply_edge_product[0]['onec']['Цены']['Цена']['Валюта'] ?? 'руб'; + + // Инициализация количества + $amount_raw = $amount = $supply['amnt'] ?? $supply_edge_product[0]['onec']['Количество']; + if (empty($amount_raw) || $amount_raw < 1) { + // Уже не используется + $amount = 'Под заказ'; + } else { + $amount .= ' шт'; + } + + if ($amount_raw < 1 || $price_raw < 1) { + // Нет в наличии или цена 0 рублей + + // Поставки отстутвуют + no_supplies: + + // Проверка на блокировку + if ($empty_block) continue; + + $supplies_html .= << + + + Заказать поиск у оператора + + + + HTML; + + // Запись в список ненайденных + $empties[] = $catn; + + // Запись блокировщика + $empty_block = true; + + // Обновление счётчика + ++$supply_iterator; + + continue; + } + + // Инициализация доставки + if (isset($delivery['error']) || $delivery === '?') { + // Не удалось рассчитать доставку + + // Инициализация типа доставки + $delivery_type = $delivery['type'] ?? 'auto'; + + // Инициализация индикатора + $delivery_icon = ''; + + // Инициализация времени + $delivery = '?'; + } else { + // Удалось рассчитать доставку + + // Инициализация типа доставки + $delivery_type = $delivery['type'] ?? 'auto'; + + // Инициализация индикатора + $delivery_icon = match ($delivery_type) { + 'avia' => '', + default => '' + }; + + // Инициализация даты отправки + try { + // Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель) + + $delivery_send_date = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspSender'])->getTimestamp(); + } catch (throwable $e) { + // Взять данные из "pickup" (Дата передачи груза на адресе отправителя) + + $delivery_send_date = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['pickup'])->getTimestamp(); + } + + // Инициализация времени доставки + try { + // Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале) + + $delivery_converted = datetime::createFromFormat('Y-m-d H:i:s', $delivery['orderDates']['giveoutFromOspReceiver'])->getTimestamp(); + } catch (Throwable $e) { + // Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель) + + $delivery_converted = datetime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspReceiver'])->getTimestamp(); + } + $delivery = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1; + } + + // Инициализация индекса аккаунта + $index = $account['indx'] ?? 'Неизвестен'; + + // Генерация + $supplies_html .= << + $index + $amount + $delivery_icon $delivery дн + $price + + + + + HTML; + + // Обновление счётчика + ++$supply_iterator; + } + + return $supplies_html; + } } diff --git a/mirzaev/skillparts/system/views/search/index.php b/mirzaev/skillparts/system/views/search/index.php index 8594eed..88cb878 100644 --- a/mirzaev/skillparts/system/views/search/index.php +++ b/mirzaev/skillparts/system/views/search/index.php @@ -3,6 +3,7 @@ declare(strict_types=1); use app\models\Product; +use app\models\Search; ?> @@ -27,218 +28,29 @@ use app\models\Product; --- HTML; - - // Обновление счётчика - ++$supply_iterator; - ?> - - - - - - - -- - + +- HTML; - - // Запись в список ненайденных - $empties[] = $catn; - - // Запись блокировщика - $empty_block = true; - - // Обновление счётчика - ++$supply_iterator; - - continue; - } - - // Инициализация доставки - if (isset($delivery['error']) || $delivery === '?') { - // Не удалось рассчитать доставку - - // Инициализация типа доставки - $delivery_type = $delivery['type'] ?? 'auto'; - - // Инициализация индикатора - $delivery_icon = ''; - - // Инициализация времени - $delivery = '?'; - } else { - // Удалось рассчитать доставку - - // Инициализация типа доставки - $delivery_type = $delivery['type'] ?? 'auto'; - - // Инициализация индикатора - $delivery_icon = match ($delivery_type) { - 'avia' => '', - default => '' - }; - - // Инициализация даты отправки - try { - // Взять данные из "arrivalToOspSender" (Дата прибытия на терминал-отправитель) - - $delivery_send_date = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspSender'])->getTimestamp(); - } catch (Throwable $e) { - // Взять данные из "pickup" (Дата передачи груза на адресе отправителя) - - $delivery_send_date = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['pickup'])->getTimestamp(); - } - - // Инициализация времени доставки - try { - // Доставка по воздуху (подразумевается), данные из "giveoutFromOspReceiver" (Дата и время, с которого груз готов к выдаче на терминале) - - $delivery_converted = DateTime::createFromFormat('Y-m-d H:i:s', $delivery['orderDates']['giveoutFromOspReceiver'])->getTimestamp(); - } catch (Throwable $e) { - // Автоматическая доставка (подразумевается), данные из "arrivalToOspReceiver" (Дата прибытия натерминал-получатель) - - $delivery_converted = DateTime::createFromFormat('Y-m-d', $delivery['orderDates']['arrivalToOspReceiver'])->getTimestamp(); - } - $delivery = ceil(($delivery_converted - ($delivery_send_date ?? 0)) / 60 / 60 / 24) + 1; - } - - // Инициализация индекса аккаунта - $index = $account['indx'] ?? 'Неизвестен'; - - // Генерация - $supplies_html .= << - $index - $amount - $delivery_icon $delivery дн - $price - - - -+ Товары по запросу: "= $query ?? 'ошибка чтения запроса' ?>"
- - - $supply_iterator) { - // Это не последняя строка с товаром и его поставками - - $supply_class_modifier = 'mb-1'; - } else { - // Это последняя строка с товаром и его поставками - - $supply_class_modifier = ''; - } - - if (is_null($supply)) { - // Поставки отсутствуют - - // Генерация данных об отсутствии заказов - goto no_supplies; - } else { - // Обычная обработка поставки - - // Инициализация переменных - extract($supply); - } - - // Инициализация цены - $price_raw = $cost; - $price = $price_raw . ' ' . $supply_edge_product[0]['onec']['Цены']['Цена']['Валюта'] ?? 'руб'; - - // Инициализация количества - $amount_raw = $amount = $supply['amnt'] ?? $supply_edge_product[0]['onec']['Количество']; - if (empty($amount_raw) || $amount_raw < 1) { - // Уже не используется - $amount = 'Под заказ'; - } else { - $amount .= ' шт'; - } - - if ($amount_raw < 1 || $price_raw < 1) { - // Нет в наличии или цена 0 рублей - - // Поставки отстутвуют - no_supplies: - - // Проверка на блокировку - if ($empty_block) continue; - - $supplies_html .= << - - - Заказать поиск у оператора - - -+- - + + +@@ -249,14 +61,56 @@ use app\models\Product;-->= $supplies_html ?> -+ + + + + +Аналогичные товары
+ + + +++ + + + + ++ + + +++ = $supplies_html ?> ++