Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 8 additions & 101 deletions core/components/minishop3/elements/snippets/ms3_products.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?php

use MiniShop3\MiniShop3;
use MiniShop3\Model\msCategory;
use MiniShop3\Model\msCategoryMember;
use MiniShop3\Model\msProduct;
use MiniShop3\Model\msProductData;
use MiniShop3\Model\msProductFile;
use MiniShop3\Model\msProductLink;
use MiniShop3\Model\msProductOption;
use MiniShop3\Model\msVendor;
use MiniShop3\Services\Category\CategoryProductScopeService;
use MiniShop3\Utils\ProductThumbnailJoin;
use MODX\Revolution\modPlugin;
use MODX\Revolution\modPluginEvent;
Expand Down Expand Up @@ -143,108 +142,16 @@
}
$pdoFetch->addTime('Conditions prepared');

// Workaround: pdoTools проверяет 'msCategory' в classMap, но MiniShop3 использует namespace
// Добавляем товары из дополнительных категорий (msCategoryMember) через кастомный WHERE
// TODO убрать этот блок в случае доработок pdoTools
// pdoTools parent filter ignores msCategoryMember; scope via CategoryProductScopeService (#481).
$_ms3Parents = (string)($scriptProperties['parents'] ?? '');
if ($_ms3Parents !== '' && $_ms3Parents !== '0') {
if ($_ms3Parents !== '' && $_ms3Parents !== '0' && $modx->services->has('ms3_category_product_scope')) {
/** @var CategoryProductScopeService $scopeService */
$scopeService = $modx->services->get('ms3_category_product_scope');
$_ms3Depth = (int)($scriptProperties['depth'] ?? 10);
$_ms3ParentsIn = [];
$_ms3ParentsOut = [];

// Разбираем parents: положительные - включить, отрицательные - исключить
foreach (array_map('trim', explode(',', $_ms3Parents)) as $_ms3Parent) {
$_ms3Parent = (int)$_ms3Parent;
if ($_ms3Parent > 0) {
$_ms3ParentsIn[] = $_ms3Parent;
} elseif ($_ms3Parent < 0) {
$_ms3ParentsOut[] = abs($_ms3Parent);
}
}

// Получаем дочерние категории для включения (только msCategory, не все ресурсы)
if (!empty($_ms3ParentsIn) && $_ms3Depth > 0) {
$_ms3CatIds = $_ms3ParentsIn;
for ($_ms3i = 0; $_ms3i < $_ms3Depth; $_ms3i++) {
$_ms3CatQuery = $modx->newQuery(msCategory::class);
$_ms3CatQuery->where([
'class_key' => msCategory::class,
'parent:IN' => $_ms3CatIds,
'published' => 1,
'deleted' => 0,
]);
$_ms3CatQuery->select('id');

if ($_ms3CatQuery->prepare() && $_ms3CatQuery->stmt->execute()) {
$_ms3ChildIds = $_ms3CatQuery->stmt->fetchAll(\PDO::FETCH_COLUMN);
if (empty($_ms3ChildIds)) {
break;
}
$_ms3ParentsIn = array_merge($_ms3ParentsIn, $_ms3ChildIds);
$_ms3CatIds = $_ms3ChildIds;
} else {
break;
}
}
}

// Получаем дочерние категории для исключения
if (!empty($_ms3ParentsOut) && $_ms3Depth > 0) {
$_ms3CatIds = $_ms3ParentsOut;
for ($_ms3i = 0; $_ms3i < $_ms3Depth; $_ms3i++) {
$_ms3CatQuery = $modx->newQuery(msCategory::class);
$_ms3CatQuery->where([
'class_key' => msCategory::class,
'parent:IN' => $_ms3CatIds,
'published' => 1,
'deleted' => 0,
]);
$_ms3CatQuery->select('id');

if ($_ms3CatQuery->prepare() && $_ms3CatQuery->stmt->execute()) {
$_ms3ChildIds = $_ms3CatQuery->stmt->fetchAll(\PDO::FETCH_COLUMN);
if (empty($_ms3ChildIds)) {
break;
}
$_ms3ParentsOut = array_merge($_ms3ParentsOut, $_ms3ChildIds);
$_ms3CatIds = $_ms3ChildIds;
} else {
break;
}
}
}

// Вычитаем исключённые категории из включённых
$_ms3ParentsIn = array_unique($_ms3ParentsIn);
$_ms3ParentsOut = array_unique($_ms3ParentsOut);
if (!empty($_ms3ParentsOut)) {
$_ms3ParentsIn = array_diff($_ms3ParentsIn, $_ms3ParentsOut);
}

// ВСЕГДА отключаем pdoTools parent processing - мы обрабатываем сами
if (!empty($_ms3ParentsIn)) {
$_ms3ParentsList = implode(',', array_map('intval', $_ms3ParentsIn));

// Получаем товары из дополнительных категорий
$_ms3MemberQuery = $modx->newQuery(msCategoryMember::class);
$_ms3MemberQuery->where(['category_id:IN' => $_ms3ParentsIn]);
$_ms3MemberQuery->select('product_id');

$_ms3MemberIds = [];
if ($_ms3MemberQuery->prepare() && $_ms3MemberQuery->stmt->execute()) {
$_ms3MemberIds = $_ms3MemberQuery->stmt->fetchAll(\PDO::FETCH_COLUMN);
}

// Строим WHERE: parent IN категориях, опционально OR id IN доп. категориях
if (!empty($_ms3MemberIds)) {
$_ms3MembersList = implode(',', array_map('intval', $_ms3MemberIds));
$where[] = "(`msProduct`.`parent` IN ({$_ms3ParentsList}) OR `msProduct`.`id` IN ({$_ms3MembersList}))";
} else {
// Нет товаров в доп. категориях - просто фильтруем по parent
$where[] = "`msProduct`.`parent` IN ({$_ms3ParentsList})";
}
$_ms3CategoryIds = $scopeService->resolveCategoryIdsFromParents($_ms3Parents, $_ms3Depth);

// ВСЕГДА отключаем стандартную фильтрацию pdoTools по parents
if ($_ms3CategoryIds !== []) {
$where[] = $scopeService->buildMsProductsWhereForCategories($_ms3CategoryIds);
$scriptProperties['parents'] = 0;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,17 @@ public function sort(array $params = []): array
}

$updated = 0;

$scope = $this->scopeService();
$scopeService = $this->scopeService();

foreach ($items as $item) {
$productId = (int) ($item['id'] ?? 0);
$menuindex = (int) ($item['menuindex'] ?? 0);

if (!$productId) {
if (!$productId || !$scopeService->canReorderInCategory($productId, $categoryId)) {
continue;
}

$product = $scope->findInCategory($categoryId, $productId, $nested);
$product = $this->modx->getObject(msProduct::class, $productId);

if ($product) {
$product->set('menuindex', $menuindex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use MiniShop3\Router\HttpStatus;
use MiniShop3\Router\Response;
use MiniShop3\Services\Category\CategoryProductScopeService;

/**
* API controller for working with product data (msProductData)
Expand Down Expand Up @@ -70,12 +71,7 @@ public function update(array $params): Response
unset($data['category_id'], $data['nested']);

if ($categoryId > 0) {
/** @var \MiniShop3\Services\Category\CategoryProductsListService|null $listService */
$listService = $this->modx->services->get('ms3_category_products_list');
if (
!$listService
|| !$listService->isProductInCategoryScope($productId, $categoryId, $nested)
) {
if (!$this->categoryProductScopeService()->findInCategory($categoryId, $productId, $nested)) {
$this->modx->lexicon->load('minishop3:default');

return Response::error(
Expand Down Expand Up @@ -107,4 +103,13 @@ public function update(array $params): Response
return Response::error('Failed to save product data: ' . $e->getMessage(), HttpStatus::INTERNAL_SERVER_ERROR);
}
}

private function categoryProductScopeService(): CategoryProductScopeService
{
$service = $this->modx->services->get('ms3_category_product_scope');

return $service instanceof CategoryProductScopeService
? $service
: new CategoryProductScopeService($this->modx);
}
}
Loading