feat: remove cache, refactor
This commit is contained in:
5
Makefile
5
Makefile
@@ -29,4 +29,7 @@ link:
|
||||
|
||||
dev:
|
||||
$(MAKE) link && \
|
||||
cd spa && npm run dev
|
||||
cd spa && npm run dev
|
||||
|
||||
lint:
|
||||
docker compose exec -w /module/oc_telegram_shop/upload/oc_telegram_shop web bash -c "./vendor/bin/phpstan analyse src framework"
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
memory: 512M
|
||||
environment:
|
||||
- WEB_DOCUMENT_ROOT=/web/upload
|
||||
- PHP_DISPLAY_ERRORS=1
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"symfony/cache": "^5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"roave/security-advisories": "dev-latest"
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"phpstan/phpstan": "^2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "329d7196ee1db3f0f4e8b3552e28f83a",
|
||||
"content-hash": "119f24aafb5ae0828f70c3c57fa07234",
|
||||
"packages": [
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
@@ -1733,6 +1733,64 @@
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.22",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4",
|
||||
"reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-04T19:17:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "roave/security-advisories",
|
||||
"version": "dev-latest",
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
|
||||
class CategoriesHandler
|
||||
{
|
||||
@@ -18,45 +16,41 @@ class CategoriesHandler
|
||||
private Builder $queryBuilder;
|
||||
private ImageToolInterface $ocImageTool;
|
||||
|
||||
public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool, Settings $settings)
|
||||
public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool)
|
||||
{
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->ocImageTool = $ocImageTool;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
$cache = new FilesystemAdapter();
|
||||
$languageId = 1;
|
||||
|
||||
$categories = $cache->get('main-page-categories', function (ItemInterface $item) {
|
||||
$item->expiresAfter($this->settings->get('cache_categories_main', 60 * 5));
|
||||
$languageId = 1;
|
||||
$categoriesFlat = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'categories.category_id' => 'id',
|
||||
'categories.parent_id' => 'parent_id',
|
||||
'categories.image' => 'image',
|
||||
'descriptions.name' => 'name',
|
||||
'descriptions.description' => 'description',
|
||||
])
|
||||
->from(db_table('category'), 'categories')
|
||||
->join(
|
||||
db_table('category_description') . ' AS descriptions',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('categories.category_id', '=', 'descriptions.category_id')
|
||||
->where('descriptions.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->where('categories.status', '=', 1)
|
||||
->orderBy('parent_id')
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
|
||||
$categoriesFlat = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'categories.category_id' => 'id',
|
||||
'categories.parent_id' => 'parent_id',
|
||||
'categories.image' => 'image',
|
||||
'descriptions.name' => 'name',
|
||||
'descriptions.description' => 'description',
|
||||
])
|
||||
->from(db_table('category'), 'categories')
|
||||
->join(
|
||||
db_table('category_description') . ' AS descriptions',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('categories.category_id', '=', 'descriptions.category_id')
|
||||
->where('descriptions.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->where('categories.status', '=', 1)
|
||||
->orderBy('parent_id')
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
$categories = $this->buildCategoryTree($categoriesFlat);
|
||||
|
||||
$categories = $this->buildCategoryTree($categoriesFlat);
|
||||
|
||||
return array_map(static function ($category) {
|
||||
return new JsonResponse([
|
||||
'data' => array_map(static function ($category) {
|
||||
return [
|
||||
'id' => (int)$category['id'],
|
||||
'image' => $category['image'],
|
||||
@@ -64,11 +58,7 @@ class CategoriesHandler
|
||||
'description' => $category['description'],
|
||||
'children' => $category['children'],
|
||||
];
|
||||
}, $categories);
|
||||
});
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $categories,
|
||||
}, $categories),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,191 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Adapters\OcModelCatalogProductAdapter;
|
||||
use App\Decorators\OcRegistryDecorator;
|
||||
use Cart\Currency;
|
||||
use Cart\Tax;
|
||||
use App\Services\ProductsService;
|
||||
use Exception;
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Support\PaginationHelper;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
use Openguru\OpenCartFramework\Logger\Logger;
|
||||
use RuntimeException;
|
||||
|
||||
class ProductsHandler
|
||||
{
|
||||
private Builder $queryBuilder;
|
||||
private Currency $currency;
|
||||
private Tax $tax;
|
||||
private Settings $settings;
|
||||
private OcModelCatalogProductAdapter $ocModelCatalogProduct;
|
||||
private ImageToolInterface $ocImageTool;
|
||||
private OcRegistryDecorator $oc;
|
||||
private ProductsService $productsService;
|
||||
private Logger $logger;
|
||||
|
||||
public function __construct(
|
||||
Builder $queryBuilder,
|
||||
Currency $currency,
|
||||
Tax $tax,
|
||||
Settings $settings,
|
||||
OcModelCatalogProductAdapter $ocModelCatalogProduct,
|
||||
ImageToolInterface $ocImageTool,
|
||||
OcRegistryDecorator $registry
|
||||
) {
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->currency = $currency;
|
||||
$this->tax = $tax;
|
||||
$this->settings = $settings;
|
||||
$this->ocModelCatalogProduct = $ocModelCatalogProduct;
|
||||
$this->ocImageTool = $ocImageTool;
|
||||
$this->oc = $registry;
|
||||
}
|
||||
|
||||
private function getProductsResponse(array $params): array
|
||||
public function __construct(Settings $settings, ProductsService $productsService, Logger $logger)
|
||||
{
|
||||
$page = $params['page'];
|
||||
$perPage = $params['perPage'];
|
||||
$categoryId = $params['categoryId'];
|
||||
$search = $params['search'];
|
||||
$forMainPage = $params['forMainPage'];
|
||||
$featuredProducts = $params['featuredProducts'];
|
||||
$mainpageProducts = $params['mainpageProducts'];
|
||||
$languageId = 1;
|
||||
$categoryName = '';
|
||||
$imageWidth = 300;
|
||||
$imageHeight = 300;
|
||||
|
||||
if ($categoryId) {
|
||||
$categoryName = $this->queryBuilder->newQuery()
|
||||
->select(['name'])
|
||||
->from(db_table('category_description'), 'category')
|
||||
->where('language_id', '=', $languageId)
|
||||
->where('category_id', '=', $categoryId)
|
||||
->value('name');
|
||||
}
|
||||
|
||||
$productsQuery = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'products.quantity' => 'product_quantity',
|
||||
'product_description.name' => 'product_name',
|
||||
'products.price' => 'price',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->when($categoryId !== 0, function (Builder $query) use ($categoryId) {
|
||||
$query->join(
|
||||
db_table('product_to_category') . ' AS product_to_category',
|
||||
function (JoinClause $join) use ($categoryId) {
|
||||
$join->on('product_to_category.product_id', '=', 'products.product_id')
|
||||
->where('product_to_category.category_id', '=', $categoryId);
|
||||
}
|
||||
);
|
||||
})
|
||||
->when(
|
||||
$forMainPage && $mainpageProducts === 'featured' && $featuredProducts,
|
||||
function (Builder $query) use ($featuredProducts) {
|
||||
$query->whereIn('products.product_id', $featuredProducts);
|
||||
}
|
||||
)
|
||||
->when($search, function (Builder $query) use ($search) {
|
||||
$query->where('product_description.name', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
|
||||
$total = $productsQuery->count();
|
||||
$lastPage = PaginationHelper::calculateLastPage($total, $perPage);
|
||||
$hasMore = $page + 1 <= $lastPage;
|
||||
|
||||
$products = $productsQuery
|
||||
->forPage($page, $perPage)
|
||||
->orderBy($mainpageProducts === 'latest' ? 'date_modified' : 'viewed', 'DESC')
|
||||
->get();
|
||||
|
||||
$productIds = Arr::pluck($products, 'product_id');
|
||||
$productsImages = [];
|
||||
|
||||
if ($productIds) {
|
||||
$productsImages = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products_images.product_id' => 'product_id',
|
||||
'products_images.image' => 'image',
|
||||
])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->orderBy('products_images.sort_order', 'ASC')
|
||||
->whereIn('product_id', $productIds)
|
||||
->get();
|
||||
}
|
||||
|
||||
$productsImagesMap = [];
|
||||
foreach ($productsImages as $item) {
|
||||
$productsImagesMap[$item['product_id']][] = [
|
||||
'url' => $this->ocImageTool->resize($item['image'], $imageWidth, $imageHeight, 'placeholder.png'),
|
||||
'alt' => 'Product Image',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight) {
|
||||
$allImages = [];
|
||||
|
||||
$image = $this->ocImageTool->resize(
|
||||
$product['product_image'],
|
||||
$imageWidth,
|
||||
$imageHeight,
|
||||
'placeholder.png'
|
||||
);
|
||||
|
||||
$allImages[] = [
|
||||
'url' => $image,
|
||||
'alt' => $product['product_name'],
|
||||
];
|
||||
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
|
||||
if (! empty($productsImagesMap[$product['product_id']])) {
|
||||
$allImages = array_merge($allImages, $productsImagesMap[$product['product_id']]);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $product['product_id'],
|
||||
'product_quantity' => (int) $product['product_quantity'],
|
||||
'name' => $product['product_name'],
|
||||
'price' => $price,
|
||||
'images' => $allImages,
|
||||
];
|
||||
}, $products),
|
||||
|
||||
'meta' => [
|
||||
'currentCategoryName' => $categoryName,
|
||||
'hasMore' => $hasMore,
|
||||
]
|
||||
];
|
||||
$this->settings = $settings;
|
||||
$this->productsService = $productsService;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function handle(Request $request): JsonResponse
|
||||
{
|
||||
$cache = new FilesystemAdapter();
|
||||
|
||||
$page = (int) $request->get('page', 1);
|
||||
$perPage = min((int) $request->get('perPage', 6), 15);
|
||||
$categoryId = (int) $request->get('categoryId', 0);
|
||||
@@ -194,45 +35,17 @@ class ProductsHandler
|
||||
$featuredProducts = $this->settings->get('featured_products');
|
||||
$mainpageProducts = $this->settings->get('mainpage_products');
|
||||
|
||||
if ($forMainPage && $page === 1) {
|
||||
$response = $cache->get(
|
||||
'main-page-products',
|
||||
function (ItemInterface $cacheitem) use (
|
||||
$page,
|
||||
$perPage,
|
||||
$categoryId,
|
||||
$search,
|
||||
$forMainPage,
|
||||
$featuredProducts,
|
||||
$mainpageProducts
|
||||
): array {
|
||||
$cacheitem->expiresAfter($this->settings->get('cache_products_main', 60 * 10));
|
||||
return $this->getProductsResponse(
|
||||
compact(
|
||||
'page',
|
||||
'perPage',
|
||||
'categoryId',
|
||||
'search',
|
||||
'forMainPage',
|
||||
'featuredProducts',
|
||||
'mainpageProducts'
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$response = $this->getProductsResponse(
|
||||
compact(
|
||||
'page',
|
||||
'perPage',
|
||||
'categoryId',
|
||||
'search',
|
||||
'forMainPage',
|
||||
'featuredProducts',
|
||||
'mainpageProducts'
|
||||
)
|
||||
);
|
||||
}
|
||||
$response = $this->productsService->getProductsResponse(
|
||||
compact(
|
||||
'page',
|
||||
'perPage',
|
||||
'categoryId',
|
||||
'search',
|
||||
'forMainPage',
|
||||
'featuredProducts',
|
||||
'mainpageProducts'
|
||||
)
|
||||
);
|
||||
|
||||
return new JsonResponse($response);
|
||||
}
|
||||
@@ -246,152 +59,22 @@ class ProductsHandler
|
||||
$imageFullWidth = 1000;
|
||||
$imageFullHeight = 1000;
|
||||
|
||||
$product = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'product_description.name' => 'product_name',
|
||||
'product_description.description' => 'product_description',
|
||||
'products.price' => 'price',
|
||||
'products.minimum' => 'minimum',
|
||||
'products.quantity' => 'quantity',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
'manufacturer.name' => 'product_manufacturer',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
db_table('manufacturer') . ' AS manufacturer',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.manufacturer_id', '=', 'manufacturer.manufacturer_id');
|
||||
}
|
||||
)
|
||||
->where('products.product_id', '=', $productId)
|
||||
->limit(1)
|
||||
->firstOrNull();
|
||||
|
||||
if (! $product) {
|
||||
return new JsonResponse([], Response::HTTP_NOT_FOUND);
|
||||
try {
|
||||
$product = $this->productsService->getProduct(
|
||||
$productId,
|
||||
$languageId,
|
||||
$imageWidth,
|
||||
$imageHeight,
|
||||
$imageFullWidth,
|
||||
$imageFullHeight
|
||||
);
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->logException($exception);
|
||||
throw new RuntimeException('Error get product with id ' . $productId);
|
||||
}
|
||||
|
||||
$productsImages = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products_images.product_id' => 'product_id',
|
||||
'products_images.image' => 'image',
|
||||
])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->orderBy('products_images.sort_order', 'ASC')
|
||||
->where('products_images.product_id', '=', $productId)
|
||||
->get();
|
||||
|
||||
$imagePaths = [];
|
||||
$imagePaths[] = $product['product_image'];
|
||||
foreach ($productsImages as $item) {
|
||||
$imagePaths[] = $item['image'];
|
||||
}
|
||||
|
||||
$images = [];
|
||||
foreach ($imagePaths as $imagePath) {
|
||||
[$width, $height] = $this->ocImageTool->getRealSize($imagePath);
|
||||
$images[] = [
|
||||
'thumbnailURL' => $this->ocImageTool->resize($imagePath, $imageWidth, $imageHeight, 'placeholder.png'),
|
||||
'largeURL' => $this->ocImageTool->resize($imagePath, $imageFullWidth, $imageFullHeight, 'placeholder.png'),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'alt' => $product['product_name'],
|
||||
];
|
||||
}
|
||||
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
|
||||
$data = [
|
||||
'id' => $product['product_id'],
|
||||
'name' => $product['product_name'],
|
||||
'description' => html_entity_decode($product['product_description']),
|
||||
'manufacturer' => $product['product_manufacturer'],
|
||||
'price' => $price,
|
||||
'minimum' => $product['minimum'],
|
||||
'quantity' => $product['quantity'],
|
||||
'images' => $images,
|
||||
'options' => $this->loadProductOptions($product),
|
||||
'attributes' => $this->loadProductAttributes($product['product_id']),
|
||||
];
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => $data,
|
||||
'data' => $product,
|
||||
]);
|
||||
}
|
||||
|
||||
private function loadProductOptions($product): array
|
||||
{
|
||||
$result = [];
|
||||
$productId = $product['product_id'];
|
||||
$taxClassId = $product['tax_class_id'];
|
||||
|
||||
$options = $this->ocModelCatalogProduct->getProductOptions($productId);
|
||||
$ocConfigTax = $this->settings->get('oc_config_tax');
|
||||
$ocDefaultCurrency = $this->settings->get('oc_default_currency');
|
||||
|
||||
foreach ($options as $option) {
|
||||
$product_option_value_data = [];
|
||||
|
||||
foreach ($option['product_option_value'] as $option_value) {
|
||||
if (! $option_value['subtract'] || ($option_value['quantity'] > 0)) {
|
||||
if ((float) $option_value['price']) {
|
||||
$priceWithTax = $this->tax->calculate(
|
||||
$option_value['price'],
|
||||
$taxClassId,
|
||||
$ocConfigTax ? 'Р' : false,
|
||||
);
|
||||
|
||||
$price = $this->currency->format($priceWithTax, $ocDefaultCurrency);
|
||||
} else {
|
||||
$price = false;
|
||||
}
|
||||
|
||||
$product_option_value_data[] = [
|
||||
'product_option_value_id' => (int) $option_value['product_option_value_id'],
|
||||
'option_value_id' => (int) $option_value['option_value_id'],
|
||||
'name' => $option_value['name'],
|
||||
'image' => $this->ocImageTool->resize($option_value['image'], 50, 50),
|
||||
'price' => $price,
|
||||
'price_prefix' => $option_value['price_prefix'],
|
||||
'selected' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'product_option_id' => (int) $option['product_option_id'],
|
||||
'values' => $product_option_value_data,
|
||||
'option_id' => (int) $option['option_id'],
|
||||
'name' => $option['name'],
|
||||
'type' => $option['type'],
|
||||
'value' => $option['value'],
|
||||
'required' => filter_var($option['required'], FILTER_VALIDATE_BOOLEAN),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function loadProductAttributes(int $productId): array
|
||||
{
|
||||
$this->oc->load->model('catalog/product');
|
||||
|
||||
return $this->oc->model_catalog_product->getProductAttributes($productId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Adapters\OcModelCatalogProductAdapter;
|
||||
use App\Decorators\OcRegistryDecorator;
|
||||
use Cart\Currency;
|
||||
use Cart\Tax;
|
||||
use Exception;
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\ImageTool\ImageToolInterface;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Builder;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Support\PaginationHelper;
|
||||
|
||||
class ProductsService
|
||||
{
|
||||
private Builder $queryBuilder;
|
||||
private Currency $currency;
|
||||
private Tax $tax;
|
||||
private Settings $settings;
|
||||
private OcModelCatalogProductAdapter $ocModelCatalogProduct;
|
||||
private ImageToolInterface $ocImageTool;
|
||||
private OcRegistryDecorator $oc;
|
||||
|
||||
public function __construct(
|
||||
Builder $queryBuilder,
|
||||
Currency $currency,
|
||||
Tax $tax,
|
||||
Settings $settings,
|
||||
OcModelCatalogProductAdapter $ocModelCatalogProduct,
|
||||
ImageToolInterface $ocImageTool,
|
||||
OcRegistryDecorator $registry
|
||||
) {
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->currency = $currency;
|
||||
$this->tax = $tax;
|
||||
$this->settings = $settings;
|
||||
$this->ocModelCatalogProduct = $ocModelCatalogProduct;
|
||||
$this->ocImageTool = $ocImageTool;
|
||||
$this->oc = $registry;
|
||||
}
|
||||
|
||||
public function getProductsResponse(array $params): array
|
||||
{
|
||||
$page = $params['page'];
|
||||
$perPage = $params['perPage'];
|
||||
$categoryId = $params['categoryId'];
|
||||
$search = $params['search'];
|
||||
$forMainPage = $params['forMainPage'];
|
||||
$featuredProducts = $params['featuredProducts'];
|
||||
$mainpageProducts = $params['mainpageProducts'];
|
||||
$languageId = 1;
|
||||
$categoryName = '';
|
||||
$imageWidth = 300;
|
||||
$imageHeight = 300;
|
||||
|
||||
if ($categoryId) {
|
||||
$categoryName = $this->queryBuilder->newQuery()
|
||||
->select(['name'])
|
||||
->from(db_table('category_description'), 'category')
|
||||
->where('language_id', '=', $languageId)
|
||||
->where('category_id', '=', $categoryId)
|
||||
->value('name');
|
||||
}
|
||||
|
||||
$productsQuery = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'products.quantity' => 'product_quantity',
|
||||
'product_description.name' => 'product_name',
|
||||
'products.price' => 'price',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->when($categoryId !== 0, function (Builder $query) use ($categoryId) {
|
||||
$query->join(
|
||||
db_table('product_to_category') . ' AS product_to_category',
|
||||
function (JoinClause $join) use ($categoryId) {
|
||||
$join->on('product_to_category.product_id', '=', 'products.product_id')
|
||||
->where('product_to_category.category_id', '=', $categoryId);
|
||||
}
|
||||
);
|
||||
})
|
||||
->when(
|
||||
$forMainPage && $mainpageProducts === 'featured' && $featuredProducts,
|
||||
function (Builder $query) use ($featuredProducts) {
|
||||
$query->whereIn('products.product_id', $featuredProducts);
|
||||
}
|
||||
)
|
||||
->when($search, function (Builder $query) use ($search) {
|
||||
$query->where('product_description.name', 'LIKE', '%' . $search . '%');
|
||||
});
|
||||
|
||||
$total = $productsQuery->count();
|
||||
$lastPage = PaginationHelper::calculateLastPage($total, $perPage);
|
||||
$hasMore = $page + 1 <= $lastPage;
|
||||
|
||||
$products = $productsQuery
|
||||
->forPage($page, $perPage)
|
||||
->orderBy($mainpageProducts === 'latest' ? 'date_modified' : 'viewed', 'DESC')
|
||||
->get();
|
||||
|
||||
$productIds = Arr::pluck($products, 'product_id');
|
||||
$productsImages = [];
|
||||
|
||||
if ($productIds) {
|
||||
$productsImages = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products_images.product_id' => 'product_id',
|
||||
'products_images.image' => 'image',
|
||||
])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->orderBy('products_images.sort_order')
|
||||
->whereIn('product_id', $productIds)
|
||||
->get();
|
||||
}
|
||||
|
||||
$productsImagesMap = [];
|
||||
foreach ($productsImages as $item) {
|
||||
$productsImagesMap[$item['product_id']][] = [
|
||||
'url' => $this->ocImageTool->resize($item['image'], $imageWidth, $imageHeight, 'placeholder.png'),
|
||||
'alt' => 'Product Image',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight) {
|
||||
$allImages = [];
|
||||
|
||||
$image = $this->ocImageTool->resize(
|
||||
$product['product_image'],
|
||||
$imageWidth,
|
||||
$imageHeight,
|
||||
'placeholder.png'
|
||||
);
|
||||
|
||||
$allImages[] = [
|
||||
'url' => $image,
|
||||
'alt' => $product['product_name'],
|
||||
];
|
||||
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
|
||||
if (! empty($productsImagesMap[$product['product_id']])) {
|
||||
$allImages = array_merge($allImages, $productsImagesMap[$product['product_id']]);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $product['product_id'],
|
||||
'product_quantity' => (int) $product['product_quantity'],
|
||||
'name' => $product['product_name'],
|
||||
'price' => $price,
|
||||
'images' => $allImages,
|
||||
];
|
||||
}, $products),
|
||||
|
||||
'meta' => [
|
||||
'currentCategoryName' => $categoryName,
|
||||
'hasMore' => $hasMore,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getProduct(
|
||||
int $productId,
|
||||
int $languageId,
|
||||
int $imageWidth,
|
||||
int $imageHeight,
|
||||
int $imageFullWidth,
|
||||
int $imageFullHeight
|
||||
): array {
|
||||
$product = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'product_description.name' => 'product_name',
|
||||
'product_description.description' => 'product_description',
|
||||
'products.price' => 'price',
|
||||
'products.minimum' => 'minimum',
|
||||
'products.quantity' => 'quantity',
|
||||
'products.image' => 'product_image',
|
||||
'products.tax_class_id' => 'tax_class_id',
|
||||
'manufacturer.name' => 'product_manufacturer',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->leftJoin(
|
||||
db_table('manufacturer') . ' AS manufacturer',
|
||||
function (JoinClause $join) {
|
||||
$join->on('products.manufacturer_id', '=', 'manufacturer.manufacturer_id');
|
||||
}
|
||||
)
|
||||
->where('products.product_id', '=', $productId)
|
||||
->limit(1)
|
||||
->firstOrNull();
|
||||
|
||||
if (! $product) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$productsImages = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products_images.product_id' => 'product_id',
|
||||
'products_images.image' => 'image',
|
||||
])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->orderBy('products_images.sort_order')
|
||||
->where('products_images.product_id', '=', $productId)
|
||||
->get();
|
||||
|
||||
$imagePaths = [];
|
||||
$imagePaths[] = $product['product_image'];
|
||||
foreach ($productsImages as $item) {
|
||||
$imagePaths[] = $item['image'];
|
||||
}
|
||||
|
||||
$images = [];
|
||||
foreach ($imagePaths as $imagePath) {
|
||||
[$width, $height] = $this->ocImageTool->getRealSize($imagePath);
|
||||
$images[] = [
|
||||
'thumbnailURL' => $this->ocImageTool->resize($imagePath, $imageWidth, $imageHeight, 'placeholder.png'),
|
||||
'largeURL' => $this->ocImageTool->resize(
|
||||
$imagePath,
|
||||
$imageFullWidth,
|
||||
$imageFullHeight,
|
||||
'placeholder.png'
|
||||
),
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'alt' => $product['product_name'],
|
||||
];
|
||||
}
|
||||
|
||||
$price = $this->currency->format(
|
||||
$this->tax->calculate(
|
||||
$product['price'],
|
||||
$product['tax_class_id'],
|
||||
$this->settings->get('oc_config_tax'),
|
||||
),
|
||||
$this->settings->get('oc_default_currency'),
|
||||
);
|
||||
|
||||
return [
|
||||
'id' => $product['product_id'],
|
||||
'name' => $product['product_name'],
|
||||
'description' => html_entity_decode($product['product_description']),
|
||||
'manufacturer' => $product['product_manufacturer'],
|
||||
'price' => $price,
|
||||
'minimum' => $product['minimum'],
|
||||
'quantity' => $product['quantity'],
|
||||
'images' => $images,
|
||||
'options' => $this->loadProductOptions($product),
|
||||
'attributes' => $this->loadProductAttributes($product['product_id']),
|
||||
];
|
||||
}
|
||||
|
||||
private function loadProductOptions($product): array
|
||||
{
|
||||
$result = [];
|
||||
$productId = $product['product_id'];
|
||||
$taxClassId = $product['tax_class_id'];
|
||||
|
||||
$options = $this->ocModelCatalogProduct->getProductOptions($productId);
|
||||
$ocConfigTax = $this->settings->get('oc_config_tax');
|
||||
$ocDefaultCurrency = $this->settings->get('oc_default_currency');
|
||||
|
||||
foreach ($options as $option) {
|
||||
$product_option_value_data = [];
|
||||
|
||||
foreach ($option['product_option_value'] as $option_value) {
|
||||
if (! $option_value['subtract'] || ($option_value['quantity'] > 0)) {
|
||||
if ((float) $option_value['price']) {
|
||||
$priceWithTax = $this->tax->calculate(
|
||||
$option_value['price'],
|
||||
$taxClassId,
|
||||
$ocConfigTax ? 'Р' : false,
|
||||
);
|
||||
|
||||
$price = $this->currency->format($priceWithTax, $ocDefaultCurrency);
|
||||
} else {
|
||||
$price = false;
|
||||
}
|
||||
|
||||
$product_option_value_data[] = [
|
||||
'product_option_value_id' => (int) $option_value['product_option_value_id'],
|
||||
'option_value_id' => (int) $option_value['option_value_id'],
|
||||
'name' => $option_value['name'],
|
||||
'image' => $this->ocImageTool->resize($option_value['image'], 50, 50),
|
||||
'price' => $price,
|
||||
'price_prefix' => $option_value['price_prefix'],
|
||||
'selected' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'product_option_id' => (int) $option['product_option_id'],
|
||||
'values' => $product_option_value_data,
|
||||
'option_id' => (int) $option['option_id'],
|
||||
'name' => $option['name'],
|
||||
'type' => $option['type'],
|
||||
'value' => $option['value'],
|
||||
'required' => filter_var($option['required'], FILTER_VALIDATE_BOOLEAN),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function loadProductAttributes(int $productId): array
|
||||
{
|
||||
$this->oc->load->model('catalog/product');
|
||||
|
||||
return $this->oc->model_catalog_product->getProductAttributes($productId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user