Есть 3 таблицы property(id, свойства продукта), property-value(id, property-id, значение свойств) и value-product(value-id, product-id):
Часть кода модели поиска:
.......
public function search($params, $id, $sort_get = null, $sort_get_limit = null) {
$query = Product::find()
->where(['category_id' => $id])
->joinWith(['brand', 'valueProducts.value', 'vidTovara', 'productComments'])
->groupBy('id');
}
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'totalCount' => $query->count(),
'defaultPageSize' => 3,
'pageSize' => $sort_get_limit,
'forcePageParam' => false,
'pageSizeParam' => false
],
'sort' => [
'attributes' => [
'price' => [
'asc' => ['price' => SORT_ASC],
'desc' => ['price' => SORT_DESC],
],
'name' => [
'asc' => ['name' => SORT_ASC],
'desc' => ['name' => SORT_DESC],
],
'id' => [
'asc' => ['id' => SORT_ASC],
'desc' => ['id' => SORT_DESC],
],
],
]
]);
$sort = (isset($sort_get) ? $sort_get : 'id');
$string = $sort_get[0];
if ($string === '-') {
$sort_minus = substr($sort, 1);
$dataProvider->sort->defaultOrder[$sort_minus] = SORT_DESC;
} else {
$dataProvider->sort->defaultOrder[$sort] = SORT_ASC;
}
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'brand.name' => $this->brand,
'new' => $this->new,
'hit' => $this->hit,
'property_values.value' => $this->property,
]);
$query->andFilterWhere([
'and',
['>=', 'price', $this->min_price],
['<=', 'price', $this->max_price],
])
->andFilterWhere([
'or',
['>', 'quantity', $this->av],
['=', 'quantity', $this->notav],
])
->andFilterWhere([
'and',
['>', 'old_price', $this->skidka],
]);
return $dataProvider;
}
}
Часть кода фильтра с формой, который в представлении выводится слева:
use yii\bootstrap4\ActiveForm;
use yii\helpers\ArrayHelper;
/* @var $this yii\web\View */
/* @var $model frontend\models\ProductSearch */
/* @var $form yii\widgets\ActiveForm */
?>
<div class="product-search">
$form = ActiveForm::begin([
'action' => ['view', 'id' => $_id],
'method' => 'get',
'options' => [
'data-pjax' => 1,
'id' => 'form_sort',
],
]);
}?>
.......
<div class="filter_zagl_div">
<?php $brand = ArrayHelper::map($category_id, 'brand.name', 'brand.name') ?>
<?php if ($brand): ?>
<span class="">Бренд</span>
<?php echo $form->field($model, 'brand')->checkboxList(ArrayHelper::map($category_id, 'brand.name', 'brand.name'))->label(false) ?>
<?php endif; ?>
<?php foreach ($property as $propertys): ?>
<span class=""><?php echo $propertys['name']; ?></span>
<?php $value = (new \frontend\models\PropertyValues())->getValues($propertys, $_id); ?>
<?php echo $form->field($model, 'property')->label(false)->checkboxList(ArrayHelper::map($value, 'value', 'value')) ?>
<?php endforeach; ?>
</div>
<?php ActiveForm::end(); ?>
</div>
Вот так это выглядит:
Фильтр работает, всё фильтруется, но не совсем так, как нужно.
Так как свойства у фильтров, начиная с "количество цветов" и ниже, находятся в одной колонке таблицы, то если выбрать, например количество цветов 12 и количество карандашей 24 то фильтр покажет и товар с 24 цветами, и товар с 12 карандашами, хотя, по идее, фильтр должен ничего не показать, так как товара одновременно с 24 цветами и 12 карандашами в базе нет.
Пробовал сделать вот таким способом:
Модель поиска (изменённый код):
..........
$query->andFilterWhere([
'property_values.value' => $this->p1,
]);
$query->andFilterWhere([
'property_values.value' => $this->p2,
]);
$query->andFilterWhere([
'property_values.value' => $this->p3,
]);
$query->andFilterWhere([
'and',
['>=', 'price', $this->min_price],
['<=', 'price', $this->max_price],
])
->andFilterWhere([
'or',
['>', 'quantity', $this->av],
['=', 'quantity', $this->notav],
])
->andFilterWhere([
'and',
['>', 'old_price', $this->skidka],
]);
return $dataProvider;
}
}
Изменённая часть кода фильтра с формой, который в представлении выводится слева:
.......
<div class="filter_zagl_div">
<?php $brand = ArrayHelper::map($category_id, 'brand.name', 'brand.name') ?>
<?php if ($brand): ?>
<span class="">Бренд</span>
<?php echo $form->field($model, 'brand')->checkboxList(ArrayHelper::map($category_id, 'brand.name', 'brand.name'))->label(false) ?>
<?php endif; ?>
<?php $i = 1; foreach ($property as $propertys): ?>
<span class=""><?php echo $propertys['name']; ?></span>
<?php $value = (new \frontend\models\PropertyValues())->getValues($propertys, $_id); ?>
<?php echo $form->field($model, 'p'.$i.'')->label(false)->checkboxList(ArrayHelper::map($value, 'value', 'value'));
if ($i == 15){ break; }$i++;?>
<?php endforeach; ?>
</div>
.......
При таком коде, если фильтровать в двух позициях, то ничего не выводится. Так как видимо идёт два запроса к одной таблице.
Выводимые данные при print_r DataProvider'а:
Фильтры цены, бренда, новинок и т.п. работают правильно, т.к. находятся в разных таблицах.
Подскажите пожалуйста, как можно сделать, чтобы фильтры из одной таблицы(из одной колонки) работали по группам. Выбрал количество цветов и 12, и 24, фильтр показал и товар с 12 цветами, и товар с 24 цветами. А если выбрал количество цветов 24 и количество карандашей 12, то фильтр показал бы товар у которого количество цветов равно 24 и количество карандашей равно 12(если есть такой товар, у меня в базе его нет), а не показывал бы два товара, у одного есть 24 цвета, а у другого 12 карандашей.
Сделал таким образом:
$query = Product::find()
->where(['category_id' => $id])
->joinWith(['brand', 'vidTovara', 'productComments'])
->leftJoin('value_product AS vp1', '`product`.`id` = `vp1`.`product_id`')
->leftJoin('value_product AS vp2', '`product`.`id` = `vp2`.`product_id`')
->leftJoin('property_values AS value1', '`vp1`.`value_id` = `value1`.`id`')
->leftJoin('property_values AS value2', '`vp2`.`value_id` = `value2`.`id`')
->groupBy('id');
......
$query->andFilterWhere([
'value1.value' => $this->p1,
]);
$query->andFilterWhere([
'value2.value' => $this->p2,
]);
Теперь фильтр работает правильно. Только как это всё нормально можно сделать, если фильтров может быть до 15-ти. Получается много LEFT JOIN.
Так тоже работает правильно.
$query = Product::find()
->where(['category_id' => $id])
->joinWith(['brand', 'vidTovara', 'productComments'])
->groupBy('id');
.............
$pv = ['p1', 'p2', 'p3'];
$i = 1; foreach ($pv as $q):
$query->leftJoin('value_product AS v'.$q.'', '`product`.`id` = `v'.$q.'`.`product_id`')
->leftJoin('property_values AS value'.$i.'', '`v'.$q.'`.`value_id` = `value'.$i.'`.`id`')
->andFilterWhere(['value'.$i.'.value' => $this->$q]);
if ($i == 15){ break; }$i++;
endforeach;
Можно ли сделать так, только подумать над переменными и условиями? Или есть другое решение?