PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 All 51 releases
fluent-cart / app / Hooks / Handlers / AttributesHandler.php

AttributesHandler.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.1, at app/Hooks/Handlers/AttributesHandler.php

193 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers;
4
5 use FluentCart\App\Models\AttributeGroup;
6 use FluentCart\App\Services\Filter\AttrGroupFilter;
7 use FluentCart\App\Services\Filter\AttrTermFilter;
8 use FluentCart\App\Services\Permission\PermissionManager;
9 use FluentCart\App\Vite;
10 use FluentCart\Database\Seeder\AttributeSeeder;
11 use FluentCart\Framework\Support\Arr;
12
13 class AttributesHandler
14 {
15 public function register()
16 {
17 // Seed the system-template attribute groups (Color, Size, …) once the
18 // attribute tables are migrated. Fires from DBMigrator::migrate() on
19 // activation/migrate; the seeder is idempotent (early-returns when any
20 // group already exists) so re-runs never duplicate merchant data.
21 add_action('fluent_cart/after_migrate', [AttributeSeeder::class, 'seed']);
22
23 add_action('fluent_cart/loading_app', function () {
24 Vite::enqueueScript('fluent_cart_attributes', 'attributes/attributes.js');
25 });
26
27 // Show Attributes under Products in the WP admin left sidebar.
28 add_action('fluent_cart/admin_submenu_added', function () {
29 global $submenu;
30 if (!isset($submenu['fluent-cart'])) {
31 return;
32 }
33
34 $capability = 'manage_options';
35 if (!current_user_can('manage_options')) {
36 $capability = PermissionManager::ADMIN_CAP;
37 }
38
39 $entry = [
40 __('↳ Attributes', 'fluent-cart'),
41 $capability,
42 'admin.php?page=fluent-cart#/products/attributes',
43 '',
44 'fluent_cart_attributes',
45 ];
46
47 $newSubmenu = [];
48 foreach ($submenu['fluent-cart'] as $key => $item) {
49 $newSubmenu[$key] = $item;
50 if ($key === 'products' && !isset($newSubmenu['attributes'])) {
51 $newSubmenu['attributes'] = $entry;
52 }
53 }
54 if (!isset($newSubmenu['attributes'])) {
55 $newSubmenu['attributes'] = $entry;
56 }
57 $submenu['fluent-cart'] = $newSubmenu;
58 }, 999); // late priority: insert Attributes right after Products AFTER the
59 // Inventory child is placed, so Attributes sits above Inventory.
60
61 add_filter('fluent_cart/admin_filter_options', function ($filterOptions, $args) {
62 $filterOptions['attr_groups_filter_options'] = AttrGroupFilter::getTableFilterOptions();
63 $filterOptions['attr_terms_filter_options'] = AttrTermFilter::getTableFilterOptions();
64 return $filterOptions;
65 }, 10, 2);
66
67 add_filter('fluent_cart/admin_table_saved_views', function ($tableConfig, $args) {
68 $filterOptions = Arr::get($args, 'filterOptions', []);
69 $tableConfig['attr_groups_table'] = ['filters' => Arr::get($filterOptions, 'attr_groups_filter_options', [])];
70 $tableConfig['attr_terms_table'] = ['filters' => Arr::get($filterOptions, 'attr_terms_filter_options', [])];
71 return $tableConfig;
72 }, 10, 2);
73
74 add_filter('fluent_cart/products_filter_options', function ($options) {
75 $options['attributes'] = [
76 'label' => __('Attributes', 'fluent-cart'),
77 'value' => 'attributes',
78 'children' => [
79 [
80 'label' => __('Attribute Term', 'fluent-cart'),
81 'value' => 'attribute_term',
82 'filter_type' => 'custom',
83 'type' => 'cascading_select',
84 'callback' => static function ($query, $item) {
85 $groupId = intval(Arr::get($item, 'value.group_id', 0));
86 $termIds = array_filter(array_map('intval', (array) Arr::get($item, 'value.term_ids', [])));
87 if (!$groupId) {
88 return;
89 }
90 $operator = Arr::get($item, 'operator', 'in');
91 $match = Arr::get($item, 'value.match', 'any');
92
93 if (empty($termIds)) {
94 // Group selected, no terms: match products that have this attribute at all
95 if ($operator === 'not_in') {
96 $query->whereDoesntHave('variants', function ($variantQuery) use ($groupId) {
97 $variantQuery->whereHas('attrRelations', function ($relQuery) use ($groupId) {
98 $relQuery->where('group_id', $groupId);
99 });
100 });
101 } else {
102 $query->whereHas('variants', function ($variantQuery) use ($groupId) {
103 $variantQuery->whereHas('attrRelations', function ($relQuery) use ($groupId) {
104 $relQuery->where('group_id', $groupId);
105 });
106 });
107 }
108 return;
109 }
110
111 if ($operator === 'not_in') {
112 $query->whereDoesntHave('variants', function ($variantQuery) use ($termIds, $groupId) {
113 $variantQuery->whereHas('attrRelations', function ($relQuery) use ($termIds, $groupId) {
114 $relQuery->where('group_id', $groupId)->whereIn('term_id', $termIds);
115 });
116 });
117 } elseif ($match === 'all') {
118 // AND: product must have a variant for each individual term
119 foreach ($termIds as $termId) {
120 $query->whereHas('variants', function ($variantQuery) use ($termId, $groupId) {
121 $variantQuery->whereHas('attrRelations', function ($relQuery) use ($termId, $groupId) {
122 $relQuery->where('group_id', $groupId)->where('term_id', $termId);
123 });
124 });
125 }
126 } else {
127 // OR: product must have a variant carrying any of the terms
128 $query->whereHas('variants', function ($variantQuery) use ($termIds, $groupId) {
129 $variantQuery->whereHas('attrRelations', function ($relQuery) use ($termIds, $groupId) {
130 $relQuery->where('group_id', $groupId)->whereIn('term_id', $termIds);
131 });
132 });
133 }
134 },
135 ],
136 ],
137 ];
138 return $options;
139 });
140
141 // Attribute groups for the cascading picker (left panel)
142 add_filter('fluent_cart/advanced_filter_options_attr_groups', function ($options, $args) {
143 $search = Arr::get($args, 'search', '');
144 $includeIds = array_filter(array_map('intval', (array) Arr::get($args, 'include_ids', [])));
145
146 $query = AttributeGroup::query()->orderBy('title', 'asc');
147
148 if ($search) {
149 $query->where('title', 'like', '%' . $search . '%');
150 }
151
152 if (!empty($includeIds)) {
153 $query->orWhereIn('id', $includeIds);
154 }
155
156 return $query->limit(200)->get()->map(function ($group) {
157 return ['id' => $group->id, 'title' => $group->title];
158 })->values()->toArray();
159 }, 10, 2);
160
161 // Terms for the selected group (right panel)
162 add_filter('fluent_cart/advanced_filter_options_attr_terms', function ($options, $args) {
163 $groupId = intval(Arr::get($args, 'parent_id', 0));
164 $search = sanitize_text_field(Arr::get($args, 'search', ''));
165 $includeIds = array_filter(array_map('intval', (array) Arr::get($args, 'include_ids', [])));
166
167 if (!$groupId) {
168 return [];
169 }
170
171 /** @var AttributeGroup $group */
172 $group = AttributeGroup::query()->find($groupId);
173 if (!$group) {
174 return [];
175 }
176
177 $query = $group->terms()->orderBy('serial', 'asc');
178
179 if ($search) {
180 $query->where('title', 'like', '%' . $search . '%');
181 }
182
183 if (!empty($includeIds)) {
184 $query->orWhereIn('id', $includeIds);
185 }
186
187 return $query->limit(50)->get()->map(function ($term) {
188 return ['id' => $term->id, 'title' => $term->title];
189 })->values()->toArray();
190 }, 10, 2);
191 }
192 }
193