Skip to content

Commit 7e0c4cb

Browse files
committed
refactor(manager): expose direct filter keys from backend
Keep manager grid filter serialization aligned with backend handlers so new direct filters do not require duplicated frontend constants.
1 parent 7ca0035 commit 7e0c4cb

6 files changed

Lines changed: 196 additions & 69 deletions

File tree

core/components/minishop3/src/Controllers/Api/Manager/CustomersController.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616
*/
1717
class CustomersController
1818
{
19+
protected const DIRECT_FILTER_KEYS = [
20+
'query',
21+
];
22+
1923
protected modX $modx;
2024

2125
public function __construct(modX $modx)
2226
{
2327
$this->modx = $modx;
2428
}
2529

30+
public static function getDirectFilterKeys(): array
31+
{
32+
return self::DIRECT_FILTER_KEYS;
33+
}
34+
2635
/**
2736
* Get list of customers with pagination and search
2837
* GET /api/mgr/customers

core/components/minishop3/src/Controllers/Api/Manager/GridConfigController.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
*/
1212
class GridConfigController
1313
{
14+
private const DIRECT_FILTER_KEY_PROVIDERS = [
15+
'orders' => OrdersController::class,
16+
'customers' => CustomersController::class,
17+
];
18+
1419
/** @var modX */
1520
protected $modx;
1621

@@ -48,7 +53,20 @@ public function getConfig(array $params): array
4853
$includeHidden = !empty($params['include_hidden']);
4954
$config = $this->service->getGridConfig($gridKey, $includeHidden);
5055

51-
return Response::success(['columns' => $config])->getData();
56+
return Response::success([
57+
'columns' => $config,
58+
'direct_filter_keys' => $this->getDirectFilterKeys($gridKey),
59+
])->getData();
60+
}
61+
62+
private function getDirectFilterKeys(string $gridKey): array
63+
{
64+
$provider = self::DIRECT_FILTER_KEY_PROVIDERS[$gridKey] ?? null;
65+
if (!$provider || !method_exists($provider, 'getDirectFilterKeys')) {
66+
return [];
67+
}
68+
69+
return $provider::getDirectFilterKeys();
5270
}
5371

5472
/**

core/components/minishop3/src/Controllers/Api/Manager/OrdersController.php

Lines changed: 76 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,29 @@
4040
*/
4141
class OrdersController
4242
{
43+
protected const DIRECT_FILTER_KEYS = [
44+
'query',
45+
'status_id',
46+
'delivery_id',
47+
'payment_id',
48+
'context_key',
49+
'createdon_from',
50+
'createdon_to',
51+
];
52+
53+
protected const DIRECT_FILTER_FIELD_MAP = [
54+
'status_id' => 'status_id',
55+
'delivery_id' => 'delivery_id',
56+
'payment_id' => 'payment_id',
57+
'context_key' => 'context',
58+
];
59+
60+
protected const ADDRESS_FILTER_KEYS = [
61+
'customer',
62+
'email',
63+
'phone',
64+
];
65+
4366
protected modX $modx;
4467
protected ?OrderLogService $orderLog = null;
4568
protected ?Utils $ms3Utils = null;
@@ -52,6 +75,11 @@ public function __construct(modX $modx)
5275
$this->loadExtraFieldsMap();
5376
}
5477

78+
public static function getDirectFilterKeys(): array
79+
{
80+
return self::DIRECT_FILTER_KEYS;
81+
}
82+
5583
/**
5684
* Get OrderLogService (lazy loading from DI)
5785
*
@@ -183,31 +211,7 @@ public function getList(array $params = []): array
183211
}
184212
}
185213

186-
// Direct filter params (from filter config)
187-
if ($statusId = ($params['status_id'] ?? null)) {
188-
$c->where(['status_id' => (int)$statusId]);
189-
}
190-
if ($deliveryId = ($params['delivery_id'] ?? null)) {
191-
$c->where(['delivery_id' => (int)$deliveryId]);
192-
}
193-
if ($paymentId = ($params['payment_id'] ?? null)) {
194-
$c->where(['payment_id' => (int)$paymentId]);
195-
}
196-
if ($contextKey = ($params['context_key'] ?? null)) {
197-
$c->where(['context' => $contextKey]);
198-
}
199-
200-
// Date range filters
201-
if ($dateFrom = ($params['createdon_from'] ?? $params['date_start'] ?? null)) {
202-
$c->where([
203-
'msOrder.createdon:>=' => date('Y-m-d 00:00:00', strtotime($dateFrom)),
204-
]);
205-
}
206-
if ($dateTo = ($params['createdon_to'] ?? $params['date_end'] ?? null)) {
207-
$c->where([
208-
'msOrder.createdon:<=' => date('Y-m-d 23:59:59', strtotime($dateTo)),
209-
]);
210-
}
214+
$this->applyDirectFilters($c, $params);
211215

212216
// Legacy params for backward compatibility
213217
if ($customer = ($params['customer'] ?? null)) {
@@ -1492,6 +1496,10 @@ protected function getOrdersStats(array $params = []): array
14921496
{
14931497
$c = $this->modx->newQuery(msOrder::class);
14941498

1499+
if ($this->hasAddressFilter($params)) {
1500+
$c->leftJoin(msOrderAddress::class, 'Address', '`Address`.order_id = msOrder.id');
1501+
}
1502+
14951503
// Filter by statuses for statistics (ms3_status_for_stat)
14961504
// Only count orders with these statuses (e.g. paid, completed)
14971505
$statusForStat = $this->modx->getOption('ms3_status_for_stat', null, '2,3');
@@ -1510,31 +1518,7 @@ protected function getOrdersStats(array $params = []): array
15101518
}
15111519
}
15121520

1513-
// Direct filter params
1514-
if ($statusId = ($params['status_id'] ?? null)) {
1515-
$c->where(['status_id' => (int)$statusId]);
1516-
}
1517-
if ($deliveryId = ($params['delivery_id'] ?? null)) {
1518-
$c->where(['delivery_id' => (int)$deliveryId]);
1519-
}
1520-
if ($paymentId = ($params['payment_id'] ?? null)) {
1521-
$c->where(['payment_id' => (int)$paymentId]);
1522-
}
1523-
if ($contextKey = ($params['context_key'] ?? null)) {
1524-
$c->where(['context' => $contextKey]);
1525-
}
1526-
1527-
// Date range filters
1528-
if ($dateFrom = ($params['createdon_from'] ?? $params['date_start'] ?? null)) {
1529-
$c->where([
1530-
'msOrder.createdon:>=' => date('Y-m-d 00:00:00', strtotime($dateFrom)),
1531-
]);
1532-
}
1533-
if ($dateTo = ($params['createdon_to'] ?? $params['date_end'] ?? null)) {
1534-
$c->where([
1535-
'msOrder.createdon:<=' => date('Y-m-d 23:59:59', strtotime($dateTo)),
1536-
]);
1537-
}
1521+
$this->applyDirectFilters($c, $params);
15381522

15391523
// Calculate sum and count
15401524
$c->select('SUM(msOrder.cost) as sum, COUNT(msOrder.id) as total');
@@ -1548,6 +1532,18 @@ protected function getOrdersStats(array $params = []): array
15481532
];
15491533
}
15501534

1535+
protected function hasAddressFilter(array $params): bool
1536+
{
1537+
foreach (self::ADDRESS_FILTER_KEYS as $fieldName) {
1538+
$value = $params['filter_' . $fieldName] ?? null;
1539+
if ($value !== null && $value !== '') {
1540+
return true;
1541+
}
1542+
}
1543+
1544+
return false;
1545+
}
1546+
15511547
/**
15521548
* Whether draft orders should be included in the manager orders list query.
15531549
*
@@ -1630,6 +1626,36 @@ protected function applyFilter($c, string $fieldName, $value): void
16301626
}
16311627
}
16321628

1629+
protected function applyDirectFilters($c, array $params): void
1630+
{
1631+
foreach (self::DIRECT_FILTER_FIELD_MAP as $paramKey => $fieldName) {
1632+
$value = $params[$paramKey] ?? null;
1633+
if ($value === null || $value === '') {
1634+
continue;
1635+
}
1636+
1637+
if (in_array($paramKey, ['status_id', 'delivery_id', 'payment_id'], true)) {
1638+
$value = (int)$value;
1639+
}
1640+
1641+
$c->where([$fieldName => $value]);
1642+
}
1643+
1644+
$dateFrom = $params['createdon_from'] ?? $params['date_start'] ?? null;
1645+
if ($dateFrom) {
1646+
$c->where([
1647+
'msOrder.createdon:>=' => date('Y-m-d 00:00:00', strtotime($dateFrom)),
1648+
]);
1649+
}
1650+
1651+
$dateTo = $params['createdon_to'] ?? $params['date_end'] ?? null;
1652+
if ($dateTo) {
1653+
$c->where([
1654+
'msOrder.createdon:<=' => date('Y-m-d 23:59:59', strtotime($dateTo)),
1655+
]);
1656+
}
1657+
}
1658+
16331659
/**
16341660
* Map sort field to database column
16351661
*
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/**
4+
* Static checks for manager grid direct filter keys (without MODX).
5+
*
6+
* Run: php tests/DirectFilterKeysTest.php
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
require __DIR__ . '/../vendor/autoload.php';
12+
13+
use MiniShop3\Controllers\Api\Manager\CustomersController;
14+
use MiniShop3\Controllers\Api\Manager\GridConfigController;
15+
use MiniShop3\Controllers\Api\Manager\OrdersController;
16+
17+
$fail = static function (string $message): never {
18+
fwrite(STDERR, "FAIL: {$message}\n");
19+
exit(1);
20+
};
21+
22+
$assertSame = static function (array $expected, array $actual, string $label) use ($fail): void {
23+
if ($actual !== $expected) {
24+
$fail(sprintf(
25+
"%s:\nexpected: %s\nactual: %s",
26+
$label,
27+
json_encode($expected, JSON_UNESCAPED_SLASHES),
28+
json_encode($actual, JSON_UNESCAPED_SLASHES)
29+
));
30+
}
31+
};
32+
33+
$assertSame(
34+
['query', 'status_id', 'delivery_id', 'payment_id', 'context_key', 'createdon_from', 'createdon_to'],
35+
OrdersController::getDirectFilterKeys(),
36+
'orders direct filter keys'
37+
);
38+
39+
$assertSame(
40+
['query'],
41+
CustomersController::getDirectFilterKeys(),
42+
'customers direct filter keys'
43+
);
44+
45+
$gridConfig = new ReflectionClass(GridConfigController::class);
46+
$providers = $gridConfig->getConstant('DIRECT_FILTER_KEY_PROVIDERS');
47+
48+
foreach (['orders' => OrdersController::class, 'customers' => CustomersController::class] as $gridKey => $provider) {
49+
if (($providers[$gridKey] ?? null) !== $provider) {
50+
$fail("grid-config provider missing for {$gridKey}");
51+
}
52+
}
53+
54+
fwrite(STDOUT, "OK DirectFilterKeysTest\n");
55+
exit(0);

vueManager/src/components/CustomersGrid.vue

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const {
4242
})
4343
4444
const columns = ref([])
45+
const directFilterKeys = ref(new Set())
4546
const loading = ref(false)
4647
const customers = ref([])
4748
const totalRecords = ref(0)
@@ -78,6 +79,15 @@ const CUSTOMER_GRID_DELETE_ACTION = {
7879
confirmAccept: 'delete',
7980
}
8081
82+
function addFilterParam(params, key, value) {
83+
if (directFilterKeys.value.has(key)) {
84+
params[key] = value
85+
return
86+
}
87+
88+
params[`filter_${key}`] = value
89+
}
90+
8191
/**
8292
* Load customers list
8393
*/
@@ -99,7 +109,7 @@ async function loadCustomers() {
99109
Object.keys(filterValues.value).forEach(key => {
100110
const value = filterValues.value[key]
101111
if (value !== null && value !== undefined && value !== '') {
102-
params[`filter_${key}`] = value
112+
addFilterParam(params, key, value)
103113
}
104114
})
105115
@@ -503,10 +513,12 @@ async function loadGridConfig() {
503513
try {
504514
const response = await request.get('/api/mgr/grid-config/customers')
505515
columns.value = response.columns || []
516+
directFilterKeys.value = new Set(response.direct_filter_keys || [])
506517
initFilters()
507518
} catch (error) {
508519
console.error('[CustomersGrid] Failed to load grid config:', error)
509520
columns.value = getDefaultColumns()
521+
directFilterKeys.value = new Set()
510522
initFilters()
511523
}
512524
}

0 commit comments

Comments
 (0)