diff --git a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php index a629b42..0f09e5b 100755 --- a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php +++ b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php @@ -65,6 +65,8 @@ class Controllerextensiontgshophandle extends Controller 'logs' => [ 'path' => DIR_LOGS, ], + 'cache_categories_main' => 60 * 10, + 'cache_products_main' => 60 * 10, ]); $app->bind(Url::class, function () { diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json index 0b1b122..57dbf32 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json @@ -23,7 +23,8 @@ "intervention/image": "^2.7", "rakit/validation": "^1.4", "vlucas/phpdotenv": "^5.6", - "guzzlehttp/guzzle": "^7.9" + "guzzlehttp/guzzle": "^7.9", + "symfony/cache": "^5.4" }, "require-dev": { "roave/security-advisories": "dev-latest" diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock index 6f3ca39..3686230 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.lock @@ -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": "7e1f9e747364eeaf0ebae0f7af3f8d2c", + "content-hash": "329d7196ee1db3f0f4e8b3552e28f83a", "packages": [ { "name": "graham-campbell/result-type", @@ -552,6 +552,55 @@ ], "time": "2024-07-20T21:41:07+00:00" }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2016-08-06T20:24:11+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -765,6 +814,56 @@ }, "time": "2023-04-04T09:54:51+00:00" }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, { "name": "rakit/validation", "version": "v1.4.0", @@ -855,6 +954,182 @@ }, "time": "2019-03-08T08:55:37+00:00" }, + { + "name": "symfony/cache", + "version": "v5.4.46", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "0fe08ee32cec2748fbfea10c52d3ee02049e0f6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/0fe08ee32cec2748fbfea10c52d3ee02049e0f6b", + "reference": "0fe08ee32cec2748fbfea10c52d3ee02049e0f6b", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/cache": "^1.0|^2.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^1.1.7|^2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<4.4", + "symfony/http-kernel": "<4.4", + "symfony/var-dumper": "<4.4" + }, + "provide": { + "psr/cache-implementation": "1.0|2.0", + "psr/simple-cache-implementation": "1.0|2.0", + "symfony/cache-implementation": "1.0|2.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "^1.6|^2.0", + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/filesystem": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v5.4.46" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-04T11:43:55+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/517c3a3619dadfa6952c4651767fcadffb4df65e", + "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/cache": "^1.0|^2.0|^3.0" + }, + "suggest": { + "symfony/cache-implementation": "" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v2.5.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:11:13+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v2.5.4", @@ -1082,6 +1357,82 @@ ], "time": "2024-12-23T08:48:59+00:00" }, + { + "name": "symfony/polyfill-php73", + "version": "v1.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-php80", "version": "v1.32.0", @@ -1162,6 +1513,140 @@ ], "time": "2025-01-02T08:10:11+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v1.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/191afdcb5804db960d26d8566b7e9a2843cab3a0", + "reference": "191afdcb5804db960d26d8566b7e9a2843cab3a0", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "suggest": { + "psr/container": "", + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v1.1.2" + }, + "time": "2019-05-28T07:50:59+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v5.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "862700068db0ddfd8c5b850671e029a90246ec75" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/862700068db0ddfd8c5b850671e029a90246ec75", + "reference": "862700068db0ddfd8c5b850671e029a90246ec75", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v5.4.45" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:11:13+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.6.2", diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CategoriesHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CategoriesHandler.php index cdb82fc..c2d6c7a 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CategoriesHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/CategoriesHandler.php @@ -2,11 +2,14 @@ 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 { @@ -15,41 +18,45 @@ class CategoriesHandler private Builder $queryBuilder; private ImageToolInterface $ocImageTool; - public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool) + public function __construct(Builder $queryBuilder, ImageToolInterface $ocImageTool, Settings $settings) { $this->queryBuilder = $queryBuilder; $this->ocImageTool = $ocImageTool; + $this->settings = $settings; } - public function index(Request $request): JsonResponse + public function index(): JsonResponse { - $languageId = 1; + $cache = new FilesystemAdapter(); - $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 = $cache->get('main-page-categories', function (ItemInterface $item) { + $item->expiresAfter($this->settings->get('cache_categories_main', 60 * 5)); + $languageId = 1; - $categories = $this->buildCategoryTree($categoriesFlat); + $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(); - return new JsonResponse([ - 'data' => array_map(static function ($category) { + $categories = $this->buildCategoryTree($categoriesFlat); + + return array_map(static function ($category) { return [ 'id' => (int)$category['id'], 'image' => $category['image'], @@ -57,7 +64,11 @@ class CategoriesHandler 'description' => $category['description'], 'children' => $category['children'], ]; - }, $categories), + }, $categories); + }); + + return new JsonResponse([ + 'data' => $categories, ]); } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php index b73379d..1cea2da 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/ProductsHandler.php @@ -15,6 +15,8 @@ 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; class ProductsHandler { @@ -44,20 +46,17 @@ class ProductsHandler $this->oc = $registry; } - public function handle(Request $request): JsonResponse + private 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; - $page = $request->get('page', 1); - $perPage = min((int)$request->get('perPage', 6), 15); - $categoryId = (int) $request->get('categoryId', 0); - $search = trim($request->get('search', '')); - $categoryName = ''; - - $forMainPage = $categoryId === 0; - $featuredProducts = $this->settings->get('featured_products'); - $mainpageProducts = $this->settings->get('mainpage_products'); - $imageWidth = 184; $imageHeight = 245; @@ -138,7 +137,7 @@ class ProductsHandler ]; } - return new JsonResponse([ + return [ 'data' => array_map(function ($product) use ($productsImagesMap, $imageWidth, $imageHeight) { $allImages = []; @@ -180,7 +179,62 @@ class ProductsHandler 'currentCategoryName' => $categoryName, 'hasMore' => $hasMore, ] - ]); + ]; + } + + 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); + $search = trim($request->get('search', '')); + $forMainPage = filter_var($request->get('forMainPage', false), FILTER_VALIDATE_BOOLEAN); + $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' + ) + ); + } + + return new JsonResponse($response); } public function show(Request $request): JsonResponse diff --git a/spa/src/components/ProductsList.vue b/spa/src/components/ProductsList.vue index f9a95ab..061b451 100644 --- a/spa/src/components/ProductsList.vue +++ b/spa/src/components/ProductsList.vue @@ -74,7 +74,7 @@ async function loadMore() { productsStore.isLoading = true; try { - const response = await productsStore.fetchProducts(categoryId, productsStore.page); + const response = await productsStore.fetchProducts(categoryId, productsStore.page, true); productsStore.hasMore = response.meta.hasMore ?? false; productsStore.products.data.push(...response.data); productsStore.products.meta.currentCategoryName = response.meta.currentCategoryName; diff --git a/spa/src/stores/ProductsStore.js b/spa/src/stores/ProductsStore.js index 11ad84e..ae0daaf 100644 --- a/spa/src/stores/ProductsStore.js +++ b/spa/src/stores/ProductsStore.js @@ -17,13 +17,14 @@ export const useProductsStore = defineStore('products', { }), actions: { - async fetchProducts(categoryId = null, page = 1) { + async fetchProducts(categoryId = null, page = 1, forMainPage = false) { try { this.isLoading = true; return await ftch('products', { categoryId: categoryId, page: page, search: this.search, + forMainPage: forMainPage, }); } catch (error) { console.error(error);