Commit 8b83e9d147ed4059002126163aaed7c775225103
1 parent
423109eb
add variantSku
Showing
5 changed files
with
412 additions
and
23 deletions
Show diff stats
1 | +<?php | |
2 | + | |
3 | +namespace artweb\artbox\ecommerce\helpers; | |
4 | + | |
5 | +use artweb\artbox\ecommerce\models\BrandLang; | |
6 | +use artweb\artbox\ecommerce\models\CategoryLang; | |
7 | +use artweb\artbox\ecommerce\models\Product; | |
8 | +use artweb\artbox\ecommerce\models\ProductLang; | |
9 | +use artweb\artbox\ecommerce\models\ProductVariant; | |
10 | +use artweb\artbox\ecommerce\models\ProductVariantLang; | |
11 | +use artweb\artbox\ecommerce\models\TaxGroup; | |
12 | +use frontend\models\Catalog; | |
13 | +use yii\base\Object; | |
14 | +use yii\db\ActiveQuery; | |
15 | +use yii\helpers\ArrayHelper; | |
16 | +use yii\elasticsearch\Query; | |
17 | + | |
18 | +class CatalogFilterHelper extends Object | |
19 | +{ | |
20 | + | |
21 | + public static $optionsList = []; | |
22 | + | |
23 | + /** | |
24 | + * Get TaxGroups | |
25 | + * | |
26 | + * @return array | |
27 | + */ | |
28 | + public static function optionsTemplate() | |
29 | + { | |
30 | + if (empty( static::$optionsList )) { | |
31 | + return static::$optionsList = ArrayHelper::getColumn( | |
32 | + TaxGroup::find() | |
33 | + ->joinWith('lang') | |
34 | + ->where([ 'is_filter' => 'TRUE' ]) | |
35 | + ->all(), | |
36 | + 'lang.alias' | |
37 | + ); | |
38 | + } else { | |
39 | + return static::$optionsList; | |
40 | + } | |
41 | + | |
42 | + } | |
43 | + | |
44 | + /** | |
45 | + * Return custom filter-option link | |
46 | + * | |
47 | + * @param array $filter | |
48 | + * @param string $key | |
49 | + * @param mixed $value | |
50 | + * @param bool $remove | |
51 | + * | |
52 | + * @return array | |
53 | + */ | |
54 | + public static function getFilterForOption(array $filter, string $key, $value, bool $remove = false) | |
55 | + { | |
56 | + | |
57 | + $optionsTemplate = self::optionsTemplate(); | |
58 | + array_unshift($optionsTemplate, "special", "brands"); | |
59 | + | |
60 | + $result = $filter; | |
61 | + | |
62 | + if (is_array($value)) { | |
63 | + foreach ($value as $value_key => $value_items) { | |
64 | + if (!is_array($value_items)) { | |
65 | + $value_items = [ $value_items ]; | |
66 | + } | |
67 | + foreach ($value_items as $value_item) { | |
68 | + if ($remove && isset( $result[ $key ] ) && ( $i = array_search( | |
69 | + $value_item, | |
70 | + $result[ $key ][ $value_key ] | |
71 | + ) ) !== false | |
72 | + ) { | |
73 | + unset( $result[ $key ][ $value_key ][ $i ] ); | |
74 | + if (empty( $result[ $key ][ $value_key ] )) { | |
75 | + unset( $result[ $key ][ $value_key ] ); | |
76 | + } | |
77 | + } else { | |
78 | + if (!isset( $result[ $key ][ $value_key ] ) || array_search( | |
79 | + $value_item, | |
80 | + $result[ $key ][ $value_key ] | |
81 | + ) === false | |
82 | + ) { | |
83 | + $result[ $key ][ $value_key ][] = $value_item; | |
84 | + } | |
85 | + } | |
86 | + } | |
87 | + } | |
88 | + } else { | |
89 | + if ($remove && isset( $result[ $key ] ) && ( $i = array_search($value, $result[ $key ]) ) !== false) { | |
90 | + unset( $result[ $key ][ $i ] ); | |
91 | + if (empty( $result[ $key ] )) { | |
92 | + unset( $result[ $key ] ); | |
93 | + } | |
94 | + } else { | |
95 | + if (!isset( $result[ $key ] ) || array_search($value, $result[ $key ]) === false) { | |
96 | + $result[ $key ][] = $value; | |
97 | + } | |
98 | + } | |
99 | + } | |
100 | + | |
101 | + $filterView = []; | |
102 | + | |
103 | + foreach ($optionsTemplate as $optionKey) { | |
104 | + if (isset( $result[ $optionKey ] )) { | |
105 | + $filterView[ $optionKey ] = $result[ $optionKey ]; | |
106 | + } | |
107 | + | |
108 | + } | |
109 | + | |
110 | + return $filterView; | |
111 | + } | |
112 | + | |
113 | + | |
114 | + | |
115 | + /** | |
116 | + * select options for product variants with selected category | |
117 | + * | |
118 | + * @param integer $categoryId | |
119 | + * @param integer $langId | |
120 | + * @return mixed | |
121 | + */ | |
122 | + public static function getProductVariantOptions($categoryId,$langId){ | |
123 | + | |
124 | + $cacheKey = [ | |
125 | + 'OptionsForFilter', | |
126 | + 'categoryId' => $categoryId, | |
127 | + 'langId' =>$langId | |
128 | + ]; | |
129 | + if (!$OptionsForFilter = \Yii::$app->cache->get($cacheKey)) { | |
130 | + $OptionsForFilter = ( new \yii\db\Query() )->distinct()->select('tax_group_lang.alias') | |
131 | + ->from('product_variant_option') | |
132 | + ->innerJoin( | |
133 | + 'tax_option', | |
134 | + 'product_variant_option.option_id = tax_option.id' | |
135 | + ) | |
136 | + ->innerJoin( | |
137 | + 'tax_group', | |
138 | + 'tax_group.id = tax_option.tax_group_id' | |
139 | + ) | |
140 | + ->innerJoin( | |
141 | + 'tax_group_lang', | |
142 | + 'tax_group_lang.tax_group_id = tax_group.id' | |
143 | + ) | |
144 | + ->innerJoin( | |
145 | + 'tax_group_to_category', | |
146 | + 'tax_group_to_category.tax_group_id = tax_group.id' | |
147 | + ) | |
148 | + ->where([ | |
149 | + 'tax_group_lang.language_id' => $langId, | |
150 | + 'tax_group_to_category.category_id' => $categoryId, | |
151 | + 'tax_group.is_filter' => true | |
152 | + ])->all(); | |
153 | + $OptionsForFilter = ArrayHelper::getColumn($OptionsForFilter,'alias'); | |
154 | + \Yii::$app->cache->set($cacheKey, $OptionsForFilter, 3600 * 24); | |
155 | + } | |
156 | + | |
157 | + return $OptionsForFilter; | |
158 | + } | |
159 | + | |
160 | + /** | |
161 | + * @param array $params | |
162 | + * @param $categoryId | |
163 | + * @param $langId | |
164 | + * @param bool $in_stock | |
165 | + * @return mixed | |
166 | + */ | |
167 | + public static function setQueryParams(array $params, $categoryId, $langId,$in_stock=true) | |
168 | + { | |
169 | + | |
170 | + $last_query = null; | |
171 | + $productVariantOptions = self::getProductVariantOptions($categoryId,$langId); | |
172 | + $filters = []; | |
173 | + foreach ($params as $key => $param) { | |
174 | + switch ($key) { | |
175 | + case 'special': | |
176 | + unset($params[$key]); | |
177 | + self::filterSpecial($param, $filters); | |
178 | + break; | |
179 | + case 'brands': | |
180 | + unset($params[$key]); | |
181 | + self::filterBrands($param, $filters); | |
182 | + break; | |
183 | + case 'prices': | |
184 | + unset($params[$key]); | |
185 | + self::filterPrices($param, $filters); | |
186 | + break; | |
187 | + | |
188 | + } | |
189 | + } | |
190 | + | |
191 | + if(!empty($params)){ | |
192 | + self::filterOptions($params, $productVariantOptions, $filters, $langId); | |
193 | + } | |
194 | + | |
195 | + | |
196 | + $filterQuery = new Query(); | |
197 | + $filterQuery->source('id'); | |
198 | + $filterQuery->from(Catalog::index(), Catalog::type()); | |
199 | + if($in_stock){ | |
200 | + $filterVO['nested']['path'] = 'variants'; | |
201 | + $filterVO['nested']['query']['bool']['must_not'][]['match']['variants.stock'] = 0; | |
202 | + $filters['bool']['must'][] = $filterVO; | |
203 | + } | |
204 | + if($filters){ | |
205 | + $filters['bool']['must'][]['term']['language_id'] = $langId; | |
206 | + $filterC['nested']['path'] = 'categories'; | |
207 | + $filterC['nested']['query']['bool']['must']['term']['categories.id'] = $categoryId; | |
208 | + $filters['bool']['must'][] = $filterC; | |
209 | + $filterQuery->query($filters); | |
210 | + } | |
211 | + return $filterQuery; | |
212 | + } | |
213 | + | |
214 | + | |
215 | + | |
216 | + /** | |
217 | + * Tax Option filter | |
218 | + * | |
219 | + * @param string[] $params | |
220 | + * @param string[] $productVariantOptions | |
221 | + * @param array $filters | |
222 | + * @param integer $langId | |
223 | + * @param bool $in_stock | |
224 | + * | |
225 | + * @return \yii\db\Query | |
226 | + */ | |
227 | + private static function filterOptions(array $params,$productVariantOptions, array &$filters, $langId, bool $in_stock = true) | |
228 | + { | |
229 | + | |
230 | + | |
231 | + $filterVO['nested']['path'] = 'variants'; | |
232 | + foreach ($params as $key=>$param){ | |
233 | + if(in_array($key, $productVariantOptions)){ | |
234 | + unset($params[$key]); | |
235 | + $filterVO['nested']['query']['bool']['filter'][]['terms']['variants.options.alias'] = $param; | |
236 | + } | |
237 | + | |
238 | + } | |
239 | + | |
240 | + if($in_stock){ | |
241 | + $filterVO['nested']['path'] = 'variants'; | |
242 | + $filterVO['nested']['query']['bool']['must_not'][]['match']['variants.stock'] = 0; | |
243 | + } | |
244 | + | |
245 | + $filters['bool']['must'][] = $filterVO; | |
246 | + | |
247 | + if(!empty($params)) { | |
248 | + | |
249 | + $filterO = []; | |
250 | + | |
251 | + foreach ($params as $key=>$param){ | |
252 | + | |
253 | + $filterO['bool']['filter'][]['terms']['options.alias'] = $param; | |
254 | + | |
255 | + } | |
256 | + | |
257 | + $filters['bool']['must'][] = $filterO; | |
258 | + } | |
259 | + | |
260 | + | |
261 | + } | |
262 | + | |
263 | + | |
264 | + | |
265 | + /** | |
266 | + * Fill $query with special filters (used in Product) | |
267 | + * | |
268 | + * @param array $params | |
269 | + * @param array $filters | |
270 | + */ | |
271 | + private static function filterSpecial(array $params, array &$filters) | |
272 | + { | |
273 | + /** | |
274 | + * @var string $key | |
275 | + */ | |
276 | + | |
277 | + foreach ($params as $key => $param) { | |
278 | + | |
279 | + $filters['bool']['must'][]['term'][$key] = $param; | |
280 | + | |
281 | + } | |
282 | + | |
283 | + } | |
284 | + | |
285 | + /** | |
286 | + * Fill query with brands filter | |
287 | + * | |
288 | + * @param int[] $params | |
289 | + * @param array $filters | |
290 | + */ | |
291 | + private static function filterBrands(array $params, array &$filters) | |
292 | + { | |
293 | + | |
294 | + | |
295 | + $filters['bool']['filter'][]['terms']['brand.alias'] = $params; | |
296 | + | |
297 | + | |
298 | + } | |
299 | + | |
300 | + /** | |
301 | + * Fill query with price limits filter | |
302 | + * | |
303 | + * @param array $params | |
304 | + * @param array $filters | |
305 | + */ | |
306 | + private static function filterPrices(array $params, array &$filters) | |
307 | + { | |
308 | + if (!empty( $params[ 'min' ] ) && $params[ 'min' ] > 0) { | |
309 | + $filters['bool']['filter']['range']['age']['gte'] = $params[ 'min' ]; | |
310 | + } | |
311 | + if (!empty( $params[ 'max' ] ) && $params[ 'max' ] > 0) { | |
312 | + $filters['bool']['filter']['range']['age']['lte'] = $params[ 'max' ]; | |
313 | + } | |
314 | + | |
315 | + } | |
316 | + | |
317 | +} | ... | ... |
helpers/FilterHelper.php
... | ... | @@ -310,7 +310,8 @@ |
310 | 310 | */ |
311 | 311 | private static function filterBrands(array $param, ActiveQuery $query) |
312 | 312 | { |
313 | - $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]); | |
313 | + $query->joinWith('brand'); | |
314 | + $query->andFilterWhere([ 'brand.alias' => $param ]); | |
314 | 315 | } |
315 | 316 | |
316 | 317 | /** | ... | ... |
models/Product.php
... | ... | @@ -40,6 +40,8 @@ |
40 | 40 | * @property ProductVariant[] $enabledVariants |
41 | 41 | * @property string $video |
42 | 42 | * @property TaxOption[] $options |
43 | + * @property TaxOption[] $filterOptions | |
44 | + * @property TaxGroup[] $groups | |
43 | 45 | * @property Brand $brand |
44 | 46 | * @property TaxOption[] $filters |
45 | 47 | * @property ProductVariant[] $variantsWithFilters |
... | ... | @@ -101,6 +103,7 @@ |
101 | 103 | */ |
102 | 104 | class Product extends ActiveRecord |
103 | 105 | { |
106 | + public $option_id; | |
104 | 107 | |
105 | 108 | public $min; |
106 | 109 | public $max; |
... | ... | @@ -366,11 +369,7 @@ |
366 | 369 | return 0; |
367 | 370 | } |
368 | 371 | } |
369 | - | |
370 | - public function getTaxOption() | |
371 | - { | |
372 | -// return $this->getOptions()->innerJoinWith('taxGroup')->where(['']) | |
373 | - } | |
372 | + | |
374 | 373 | |
375 | 374 | /** |
376 | 375 | * Get Product name concatenated with Brand name |
... | ... | @@ -472,9 +471,37 @@ |
472 | 471 | public function getOptions() |
473 | 472 | { |
474 | 473 | return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) |
475 | - ->viaTable('product_option', [ 'product_id' => 'id' ]); | |
474 | + ->viaTable('product_option', [ 'product_id' => 'id' ]); | |
475 | + | |
476 | 476 | } |
477 | - | |
477 | + | |
478 | + | |
479 | + /** | |
480 | + * Get TaxOptions query for current Product that will by used in filter | |
481 | + * | |
482 | + * @return ActiveQuery | |
483 | + */ | |
484 | + public function getFilterOptions() | |
485 | + { | |
486 | + | |
487 | + return $this->getOptions() | |
488 | + ->joinWith(['lang','taxGroup']) | |
489 | + ->where(['is_filter' => true]); | |
490 | + } | |
491 | + | |
492 | + | |
493 | + /** | |
494 | + * Get TaxGroup for current Product | |
495 | + * | |
496 | + * @return ActiveQuery | |
497 | + */ | |
498 | + public function getGroups() | |
499 | + { | |
500 | + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) | |
501 | + ->via('options'); | |
502 | + } | |
503 | + | |
504 | + | |
478 | 505 | /** |
479 | 506 | * Get TaxOptions query for current Product joined with TaxGroups |
480 | 507 | * | ... | ... |
models/ProductFrontendSearch.php
... | ... | @@ -2,6 +2,7 @@ |
2 | 2 | |
3 | 3 | namespace artweb\artbox\ecommerce\models; |
4 | 4 | |
5 | + use artweb\artbox\ecommerce\helpers\CatalogFilterHelper; | |
5 | 6 | use artweb\artbox\ecommerce\helpers\FilterHelper; |
6 | 7 | use artweb\artbox\ecommerce\models\Category; |
7 | 8 | use artweb\artbox\language\models\Language; |
... | ... | @@ -174,7 +175,19 @@ |
174 | 175 | ); |
175 | 176 | |
176 | 177 | $lang = Language::getCurrent(); |
177 | - FilterHelper::setQueryParams($query, $params, $category->id, $lang->id); | |
178 | + | |
179 | + /** | |
180 | + * @var $catalog \yii\elasticsearch\Query; | |
181 | + */ | |
182 | + if($params){ | |
183 | + $catalog = CatalogFilterHelper::setQueryParams($params, $category->id, $lang->id); | |
184 | + $catalog->limit(9999); | |
185 | + $catalog->createCommand(); | |
186 | + $result = $catalog->column('id'); | |
187 | + $query->andWhere(['product.id'=>$result]); | |
188 | + } | |
189 | + | |
190 | + | |
178 | 191 | return $query; |
179 | 192 | } |
180 | 193 | |
... | ... | @@ -194,21 +207,27 @@ |
194 | 207 | $query = Product::find(); |
195 | 208 | } |
196 | 209 | |
210 | + | |
211 | + | |
197 | 212 | $query->select(['MAX('.ProductVariant::tableName() . '.price) as max', 'MIN('.ProductVariant::tableName() . '.price) as min']); |
198 | 213 | $query->joinWith('variant'); |
199 | - | |
214 | + | |
200 | 215 | // Price filter fix |
201 | 216 | unset( $params[ 'prices' ] ); |
202 | - | |
203 | 217 | $lang = Language::getCurrent(); |
204 | - FilterHelper::setQueryParams($query, $params, $category->id, $lang->id); | |
205 | - $query->andWhere( | |
206 | - [ | |
207 | - '>=', | |
208 | - ProductVariant::tableName() . '.stock', | |
209 | - 1, | |
210 | - ] | |
211 | - ); | |
218 | + | |
219 | + /** | |
220 | + * @var $catalog \yii\elasticsearch\Query; | |
221 | + */ | |
222 | + if($params){ | |
223 | + $catalog = CatalogFilterHelper::setQueryParams($params, $category->id, $lang->id); | |
224 | + $catalog->limit(9999); | |
225 | + $catalog->createCommand(); | |
226 | + $result = $catalog->column('id'); | |
227 | + $query->andWhere(['product.id'=>$result]); | |
228 | + } | |
229 | + | |
230 | + | |
212 | 231 | return $query->one(); |
213 | 232 | } |
214 | 233 | } |
215 | 234 | \ No newline at end of file | ... | ... |
models/ProductVariant.php
... | ... | @@ -24,6 +24,8 @@ |
24 | 24 | * @property integer $product_unit_id |
25 | 25 | * @property string $fullname |
26 | 26 | * @property TaxOption[] $options |
27 | + * @property TaxGroup[] $groups | |
28 | + * @property TaxOption[] $filterOptions | |
27 | 29 | * @property ProductUnit $productUnit |
28 | 30 | * @property Product $product |
29 | 31 | * @property Category[] $categories |
... | ... | @@ -71,7 +73,7 @@ |
71 | 73 | { |
72 | 74 | |
73 | 75 | public $customOption = []; |
74 | - | |
76 | + | |
75 | 77 | /** |
76 | 78 | * @var int[] $options |
77 | 79 | */ |
... | ... | @@ -280,7 +282,30 @@ |
280 | 282 | return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) |
281 | 283 | ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]); |
282 | 284 | } |
283 | - | |
285 | + | |
286 | + /** | |
287 | + * Get TaxGroup for current Product | |
288 | + * | |
289 | + * @return ActiveQuery | |
290 | + */ | |
291 | + public function getGroups() | |
292 | + { | |
293 | + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) | |
294 | + ->via('options'); | |
295 | + } | |
296 | + | |
297 | + /** | |
298 | + * Get TaxOptions query for current ProductVariant that will by used in filter | |
299 | + * | |
300 | + * @return ActiveQuery | |
301 | + */ | |
302 | + public function getFilterOptions() | |
303 | + { | |
304 | + return $this->getOptions() | |
305 | + ->joinWith(['lang','taxGroup']) | |
306 | + ->where(['is_filter' => true]); | |
307 | + } | |
308 | + | |
284 | 309 | /** |
285 | 310 | * Get one variant's option whith needed conditions, or random if condition is empty |
286 | 311 | * |
... | ... | @@ -332,7 +357,7 @@ |
332 | 357 | { |
333 | 358 | $this->options = $values; |
334 | 359 | } |
335 | - | |
360 | + | |
336 | 361 | /** |
337 | 362 | * Get all TaxGroups for current ProductVariant filled with $customOptions that satisfy current ProductVariant |
338 | 363 | * |
... | ... | @@ -446,7 +471,7 @@ |
446 | 471 | } |
447 | 472 | } |
448 | 473 | } |
449 | - | |
474 | + | |
450 | 475 | /** |
451 | 476 | * @return string |
452 | 477 | */ | ... | ... |