diff --git a/helpers/CatalogFilterHelper.php b/helpers/CatalogFilterHelper.php new file mode 100644 index 0000000..d0c0665 --- /dev/null +++ b/helpers/CatalogFilterHelper.php @@ -0,0 +1,317 @@ +joinWith('lang') + ->where([ 'is_filter' => 'TRUE' ]) + ->all(), + 'lang.alias' + ); + } else { + return static::$optionsList; + } + + } + + /** + * Return custom filter-option link + * + * @param array $filter + * @param string $key + * @param mixed $value + * @param bool $remove + * + * @return array + */ + public static function getFilterForOption(array $filter, string $key, $value, bool $remove = false) + { + + $optionsTemplate = self::optionsTemplate(); + array_unshift($optionsTemplate, "special", "brands"); + + $result = $filter; + + if (is_array($value)) { + foreach ($value as $value_key => $value_items) { + if (!is_array($value_items)) { + $value_items = [ $value_items ]; + } + foreach ($value_items as $value_item) { + if ($remove && isset( $result[ $key ] ) && ( $i = array_search( + $value_item, + $result[ $key ][ $value_key ] + ) ) !== false + ) { + unset( $result[ $key ][ $value_key ][ $i ] ); + if (empty( $result[ $key ][ $value_key ] )) { + unset( $result[ $key ][ $value_key ] ); + } + } else { + if (!isset( $result[ $key ][ $value_key ] ) || array_search( + $value_item, + $result[ $key ][ $value_key ] + ) === false + ) { + $result[ $key ][ $value_key ][] = $value_item; + } + } + } + } + } else { + if ($remove && isset( $result[ $key ] ) && ( $i = array_search($value, $result[ $key ]) ) !== false) { + unset( $result[ $key ][ $i ] ); + if (empty( $result[ $key ] )) { + unset( $result[ $key ] ); + } + } else { + if (!isset( $result[ $key ] ) || array_search($value, $result[ $key ]) === false) { + $result[ $key ][] = $value; + } + } + } + + $filterView = []; + + foreach ($optionsTemplate as $optionKey) { + if (isset( $result[ $optionKey ] )) { + $filterView[ $optionKey ] = $result[ $optionKey ]; + } + + } + + return $filterView; + } + + + + /** + * select options for product variants with selected category + * + * @param integer $categoryId + * @param integer $langId + * @return mixed + */ + public static function getProductVariantOptions($categoryId,$langId){ + + $cacheKey = [ + 'OptionsForFilter', + 'categoryId' => $categoryId, + 'langId' =>$langId + ]; + if (!$OptionsForFilter = \Yii::$app->cache->get($cacheKey)) { + $OptionsForFilter = ( new \yii\db\Query() )->distinct()->select('tax_group_lang.alias') + ->from('product_variant_option') + ->innerJoin( + 'tax_option', + 'product_variant_option.option_id = tax_option.id' + ) + ->innerJoin( + 'tax_group', + 'tax_group.id = tax_option.tax_group_id' + ) + ->innerJoin( + 'tax_group_lang', + 'tax_group_lang.tax_group_id = tax_group.id' + ) + ->innerJoin( + 'tax_group_to_category', + 'tax_group_to_category.tax_group_id = tax_group.id' + ) + ->where([ + 'tax_group_lang.language_id' => $langId, + 'tax_group_to_category.category_id' => $categoryId, + 'tax_group.is_filter' => true + ])->all(); + $OptionsForFilter = ArrayHelper::getColumn($OptionsForFilter,'alias'); + \Yii::$app->cache->set($cacheKey, $OptionsForFilter, 3600 * 24); + } + + return $OptionsForFilter; + } + + /** + * @param array $params + * @param $categoryId + * @param $langId + * @param bool $in_stock + * @return mixed + */ + public static function setQueryParams(array $params, $categoryId, $langId,$in_stock=true) + { + + $last_query = null; + $productVariantOptions = self::getProductVariantOptions($categoryId,$langId); + $filters = []; + foreach ($params as $key => $param) { + switch ($key) { + case 'special': + unset($params[$key]); + self::filterSpecial($param, $filters); + break; + case 'brands': + unset($params[$key]); + self::filterBrands($param, $filters); + break; + case 'prices': + unset($params[$key]); + self::filterPrices($param, $filters); + break; + + } + } + + if(!empty($params)){ + self::filterOptions($params, $productVariantOptions, $filters, $langId); + } + + + $filterQuery = new Query(); + $filterQuery->source('id'); + $filterQuery->from(Catalog::index(), Catalog::type()); + if($in_stock){ + $filterVO['nested']['path'] = 'variants'; + $filterVO['nested']['query']['bool']['must_not'][]['match']['variants.stock'] = 0; + $filters['bool']['must'][] = $filterVO; + } + if($filters){ + $filters['bool']['must'][]['term']['language_id'] = $langId; + $filterC['nested']['path'] = 'categories'; + $filterC['nested']['query']['bool']['must']['term']['categories.id'] = $categoryId; + $filters['bool']['must'][] = $filterC; + $filterQuery->query($filters); + } + return $filterQuery; + } + + + + /** + * Tax Option filter + * + * @param string[] $params + * @param string[] $productVariantOptions + * @param array $filters + * @param integer $langId + * @param bool $in_stock + * + * @return \yii\db\Query + */ + private static function filterOptions(array $params,$productVariantOptions, array &$filters, $langId, bool $in_stock = true) + { + + + $filterVO['nested']['path'] = 'variants'; + foreach ($params as $key=>$param){ + if(in_array($key, $productVariantOptions)){ + unset($params[$key]); + $filterVO['nested']['query']['bool']['filter'][]['terms']['variants.options.alias'] = $param; + } + + } + + if($in_stock){ + $filterVO['nested']['path'] = 'variants'; + $filterVO['nested']['query']['bool']['must_not'][]['match']['variants.stock'] = 0; + } + + $filters['bool']['must'][] = $filterVO; + + if(!empty($params)) { + + $filterO = []; + + foreach ($params as $key=>$param){ + + $filterO['bool']['filter'][]['terms']['options.alias'] = $param; + + } + + $filters['bool']['must'][] = $filterO; + } + + + } + + + + /** + * Fill $query with special filters (used in Product) + * + * @param array $params + * @param array $filters + */ + private static function filterSpecial(array $params, array &$filters) + { + /** + * @var string $key + */ + + foreach ($params as $key => $param) { + + $filters['bool']['must'][]['term'][$key] = $param; + + } + + } + + /** + * Fill query with brands filter + * + * @param int[] $params + * @param array $filters + */ + private static function filterBrands(array $params, array &$filters) + { + + + $filters['bool']['filter'][]['terms']['brand.alias'] = $params; + + + } + + /** + * Fill query with price limits filter + * + * @param array $params + * @param array $filters + */ + private static function filterPrices(array $params, array &$filters) + { + if (!empty( $params[ 'min' ] ) && $params[ 'min' ] > 0) { + $filters['bool']['filter']['range']['age']['gte'] = $params[ 'min' ]; + } + if (!empty( $params[ 'max' ] ) && $params[ 'max' ] > 0) { + $filters['bool']['filter']['range']['age']['lte'] = $params[ 'max' ]; + } + + } + +} diff --git a/helpers/FilterHelper.php b/helpers/FilterHelper.php index 820ce46..f327323 100755 --- a/helpers/FilterHelper.php +++ b/helpers/FilterHelper.php @@ -310,7 +310,8 @@ */ private static function filterBrands(array $param, ActiveQuery $query) { - $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]); + $query->joinWith('brand'); + $query->andFilterWhere([ 'brand.alias' => $param ]); } /** diff --git a/models/Product.php b/models/Product.php index 3ce1249..dd5c0e7 100755 --- a/models/Product.php +++ b/models/Product.php @@ -40,6 +40,8 @@ * @property ProductVariant[] $enabledVariants * @property string $video * @property TaxOption[] $options + * @property TaxOption[] $filterOptions + * @property TaxGroup[] $groups * @property Brand $brand * @property TaxOption[] $filters * @property ProductVariant[] $variantsWithFilters @@ -101,6 +103,7 @@ */ class Product extends ActiveRecord { + public $option_id; public $min; public $max; @@ -366,11 +369,7 @@ return 0; } } - - public function getTaxOption() - { -// return $this->getOptions()->innerJoinWith('taxGroup')->where(['']) - } + /** * Get Product name concatenated with Brand name @@ -472,9 +471,37 @@ public function getOptions() { return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) - ->viaTable('product_option', [ 'product_id' => 'id' ]); + ->viaTable('product_option', [ 'product_id' => 'id' ]); + } - + + + /** + * Get TaxOptions query for current Product that will by used in filter + * + * @return ActiveQuery + */ + public function getFilterOptions() + { + + return $this->getOptions() + ->joinWith(['lang','taxGroup']) + ->where(['is_filter' => true]); + } + + + /** + * Get TaxGroup for current Product + * + * @return ActiveQuery + */ + public function getGroups() + { + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->via('options'); + } + + /** * Get TaxOptions query for current Product joined with TaxGroups * diff --git a/models/ProductFrontendSearch.php b/models/ProductFrontendSearch.php index cd13d35..e17496b 100755 --- a/models/ProductFrontendSearch.php +++ b/models/ProductFrontendSearch.php @@ -2,6 +2,7 @@ namespace artweb\artbox\ecommerce\models; + use artweb\artbox\ecommerce\helpers\CatalogFilterHelper; use artweb\artbox\ecommerce\helpers\FilterHelper; use artweb\artbox\ecommerce\models\Category; use artweb\artbox\language\models\Language; @@ -174,7 +175,19 @@ ); $lang = Language::getCurrent(); - FilterHelper::setQueryParams($query, $params, $category->id, $lang->id); + + /** + * @var $catalog \yii\elasticsearch\Query; + */ + if($params){ + $catalog = CatalogFilterHelper::setQueryParams($params, $category->id, $lang->id); + $catalog->limit(9999); + $catalog->createCommand(); + $result = $catalog->column('id'); + $query->andWhere(['product.id'=>$result]); + } + + return $query; } @@ -194,21 +207,27 @@ $query = Product::find(); } + + $query->select(['MAX('.ProductVariant::tableName() . '.price) as max', 'MIN('.ProductVariant::tableName() . '.price) as min']); $query->joinWith('variant'); - + // Price filter fix unset( $params[ 'prices' ] ); - $lang = Language::getCurrent(); - FilterHelper::setQueryParams($query, $params, $category->id, $lang->id); - $query->andWhere( - [ - '>=', - ProductVariant::tableName() . '.stock', - 1, - ] - ); + + /** + * @var $catalog \yii\elasticsearch\Query; + */ + if($params){ + $catalog = CatalogFilterHelper::setQueryParams($params, $category->id, $lang->id); + $catalog->limit(9999); + $catalog->createCommand(); + $result = $catalog->column('id'); + $query->andWhere(['product.id'=>$result]); + } + + return $query->one(); } } \ No newline at end of file diff --git a/models/ProductVariant.php b/models/ProductVariant.php index cf2a496..19ca0cc 100755 --- a/models/ProductVariant.php +++ b/models/ProductVariant.php @@ -24,6 +24,8 @@ * @property integer $product_unit_id * @property string $fullname * @property TaxOption[] $options + * @property TaxGroup[] $groups + * @property TaxOption[] $filterOptions * @property ProductUnit $productUnit * @property Product $product * @property Category[] $categories @@ -71,7 +73,7 @@ { public $customOption = []; - + /** * @var int[] $options */ @@ -280,7 +282,30 @@ return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]); } - + + /** + * Get TaxGroup for current Product + * + * @return ActiveQuery + */ + public function getGroups() + { + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->via('options'); + } + + /** + * Get TaxOptions query for current ProductVariant that will by used in filter + * + * @return ActiveQuery + */ + public function getFilterOptions() + { + return $this->getOptions() + ->joinWith(['lang','taxGroup']) + ->where(['is_filter' => true]); + } + /** * Get one variant's option whith needed conditions, or random if condition is empty * @@ -332,7 +357,7 @@ { $this->options = $values; } - + /** * Get all TaxGroups for current ProductVariant filled with $customOptions that satisfy current ProductVariant * @@ -446,7 +471,7 @@ } } } - + /** * @return string */ -- libgit2 0.21.4