Фильтрация значений методом GET

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
Ответить
pioneer
Сообщения: 136
Зарегистрирован: 2013.03.10, 23:27

Фильтрация значений методом GET

Сообщение pioneer »

Здравствуйте, уважаемые форумчане!

Решил внедрить в разрабатываемый интернет-магазин вот такую систему фильтров:
1. Администратор сайта создает группу фильтров, например, "Материал" и указывает, что её необходимо выводить на странице "Мягкая мебель".
2. Создает значения для фильтра и привязывает их к группе. Например, значения "Дерево", "Металл" и "Пластик" - привязывает к раннее созданной группе фильтров "Материал".
3. При создании или редактировании товара администратор теперь лишь устанавливает необходимые значения фильтров для данного товара. Например, данный создаваемый/редактируемый товар содержит следующие значения фильтров: "Дерево" (группа фильтров "Материал") и "Черный" (группа фильтров "Цвет").
Таким образом, рядовой пользователь, зайдя на страницу мягкой мебели в интернет-магазине увидит ровно те наборы фильтров, которые предпочел установить там администратор - все просто, логично и удобно.

Перед описанием самой проблемы я на всякий случай приведу необходимый описательный фрагмент структуры БД:

category - таблица категорий: |id|name|...
good - таблица товаров: |id|name|...
filter_group - таблица "группа фильтров": |id|name|...
filter_group_category - промежуточная таблица - |id|filter_group_id|category_id| - для связки таблиц category и filter_group отношением "многие-ко-многим" (т.е. определенная группа фильтров может выводиться в произвольном числе категорий)
filter_item - таблица "значение фильтра": |id|name|filter_group_id|, где filter_group_id - внешний ключ, ссылающийся на поле id в таблице filter_group
filter_item_good - промежуточная таблица - |id|filter_item_id|good_id| - для связки таблиц filter_item и good отношением "многие-ко-многим" (т.е. у определенного товара может быть сколь угодно значений фильтров)

А теперь сама суть проблемы: раньше каждый из фильтров выводился на странице категории в виде выпадающего списка (находясь при этом внутри <form></form>), и при изменении значения в нем форма автоматически сабмитилась GET-запросом, после чего товары сортировались согласно выбранным фильтрам, и при этом отличительная особенность "старого подхода" в том, что все эти фильтры были обособленны - у каждого из <select>'ов был свой name, согласно которому фильтры правильно и применялись, и по итогу URL был где-то таким:

mysite.ru/category/1?Good[color]=1&Good[country]=2 - думаю, тут суть ясна

А теперь логика совершенно другая - необходимо в цикле вывести на страницу набор групп фильтров (и связных с ними значений фильтров), которые непосредственно относятся к данной категории - с этим у меня проблем не возникло, а сложности возникли тогда, когда стал вопрос о необходимости правильной сортировки товаров, ведь URL теперь принял вот такой вид:

mysite.com/category/1?Good[filtersArray][]=1&Good[filtersArray][]=2

- таким он получился вот из-за этого:

Код: Выделить всё

<?php echo CHtml::activeDropDownList($goods, 'filtersArray[]', $filterGroup->getClientFilterItems()); ?>
При этом я пытаюсь получить массив фильтров и поместить его в WHERE filter_item_id IN({то_что_пришло_из_формы}) - при первом выборе какого-либо значения из выпадающего списка все вполне корректно фильтруется, однако потом начинается полный бред: во-первых как я уже заметил в моем подходе нет четкого разделения на группы фильтров, т.е. если я к примеру выберу "фильтровать по цвету: черный", url станет вот таким:

mysite.com/category/1?Good[filtersArray][]=1, где 1 - filter_item_id - соотв. фильтру "черный" группы фильтров "Цвет"

но если позже я выберу буквально в том же выпадающем списке "фильтровать по цвету: белый", то теперь url станет вот таким:

mysite.com/category/1?Good[filtersArray][]=1&Good[filtersArray][]=2 - что вполне логично, увы, выбор "черного цвета" никуда не делся

а во-вторых (что, видимо, следует из "во-первых") - выбранное значение в выпадающем списке не сохраняется после сабмита формы :(

В общем, я очень надеюсь, что мою проблему вы поняли и сможете мне помочь дельным советом. Я извиняюсь за тему в "многобукоф", но это было необходимо для того, чтобы я однозначно и как можно проще и понятнее донес суть проблемы :)

Большое спасибо заранее! ;)
Аватара пользователя
skeef
Сообщения: 67
Зарегистрирован: 2010.10.30, 20:41

Re: Фильтрация значений методом GET

Сообщение skeef »

Может я чего не понял ... но зачем использовать массивы в Url = GET -?
Если бы вы использовали, к примеру Url вида (в вашем стиле):

Код: Выделить всё

mysite.com/category/1?good_color=1

а наверное лучше или правильней было бы:

Код: Выделить всё

mysite.com/category/1/good_color/1

при этом GET нормально интерпретирует good_color как переменную и теперь
при выборе нового цвета будет изменять эту переменную, а не добавлять новое значение в массив Good[filtersArray][] ...
И еще, мне кажется вам придется уйти от массивов в сторону переменных, т.к. длина GET ограничена
Processor not found! Software emulation ...
pioneer
Сообщения: 136
Зарегистрирован: 2013.03.10, 23:27

Re: Фильтрация значений методом GET

Сообщение pioneer »

skeef писал(а):Может я чего не понял ... но зачем использовать массивы в Url = GET -?
Если бы вы использовали, к примеру Url вида (в вашем стиле):

Код: Выделить всё

mysite.com/category/1?good_color=1

а наверное лучше или правильней было бы:

Код: Выделить всё

mysite.com/category/1/good_color/1

при этом GET нормально интерпретирует good_color как переменную и теперь
при выборе нового цвета будет изменять эту переменную, а не добавлять новое значение в массив Good[filtersArray][] ...
И еще, мне кажется вам придется уйти от массивов в сторону переменных, т.к. длина GET ограничена
Просто все дело в том, что переменные нужно заранее именовать и знать "что ожидать" в

Код: Выделить всё

if(isset($_GET['Good']['good_color'])) {...}
а у меня в клиентской части выводится произвольное число наборов групп фильтров (т.е. там может быть только фильтр "по цвету", или "по материалу", или сразу оба) - это следует из той структуры БД и логики, которую я описал в первом сообщении...а так конечно да - так, как вы написали раньше и было.
Аватара пользователя
skeef
Сообщения: 67
Зарегистрирован: 2010.10.30, 20:41

Re: Фильтрация значений методом GET

Сообщение skeef »

Ну скажите мне какая разница: передавать, например, массив $Good[filtersArray]['color'=>'6', 'size'=>'44](синтаксис не верный, но вы меня поняли) или две переменные, которые можно формировать динамически ($$var еще никто не отменял) $good_color и $good_size или даже $filters_good_color и $filters_good_size если так важна filters (в данном случае мы говорим про передачу методом GET). Не стоит получаемая путаница, сложность и прочее того мнимого удобства что вы получаете.
Еще раз говорю: метод GET ораничен если не ошибаюсь 256 символами. Переменная занимает меньше строковых символов чем массив однозначно. Далее, вы пытаетесь решить проблему передачи ссылки с параметрами другому пользователю, при этом гробите SEO на корню. Никому не понятно ?Good[filtersArray][]=1&Good[filtersArray][]=2, с другой стороны goods_color/6/goods_size/44 понятен всем
Посмотрите вот это: http://php.ru/forum/viewtopic.php?t=13918 - почти по вашему вопоросу
Если, к примеру, забить на seo и при передаче набора фильтров другим юзерам - я сделал бы по типу Youtube:
- создал табличку "ссылки"
- на сайте кнопочку типа "Создать короткую ссылку на набор фильтров" - ну как-то так
- и генерил ссылку типа: mysite/shortfilterurl/ssdRRto44h
далее понятно.
Как вариант и возможно я ошибаюсь :)
Processor not found! Software emulation ...
pioneer
Сообщения: 136
Зарегистрирован: 2013.03.10, 23:27

Re: Фильтрация значений методом GET

Сообщение pioneer »

skeef писал(а):Ну скажите мне какая разница: передавать, например, массив $Good[filtersArray]['color'=>'6', 'size'=>'44](синтаксис не верный, но вы меня поняли) или две переменные, которые можно формировать динамически ($$var еще никто не отменял) $good_color и $good_size или даже $filters_good_color и $filters_good_size если так важна filters (в данном случае мы говорим про передачу методом GET). Не стоит получаемая путаница, сложность и прочее того мнимого удобства что вы получаете.
Еще раз говорю: метод GET ораничен если не ошибаюсь 256 символами. Переменная занимает меньше строковых символов чем массив однозначно. Далее, вы пытаетесь решить проблему передачи ссылки с параметрами другому пользователю, при этом гробите SEO на корню. Никому не понятно ?Good[filtersArray][]=1&Good[filtersArray][]=2, с другой стороны goods_color/6/goods_size/44 понятен всем
Посмотрите вот это: http://php.ru/forum/viewtopic.php?t=13918 - почти по вашему вопоросу
Если, к примеру, забить на seo и при передаче набора фильтров другим юзерам - я сделал бы по типу Youtube:
- создал табличку "ссылки"
- на сайте кнопочку типа "Создать короткую ссылку на набор фильтров" - ну как-то так
- и генерил ссылку типа: mysite/shortfilterurl/ssdRRto44h
далее понятно.
Как вариант и возможно я ошибаюсь :)
Либо я плохо объяснил, либо вас плохо понимаю, но в вашем примере мне якобы должен быть заранее известен набор фильтров - соответственно, их точное количество и имена переменных. То есть выражаясь более простым языком в форме, где "крутятся" сами фильтры это должно выглядеть как-то так:

Код: Выделить всё

echo CHtml::activeDropDownList($goods, 'goods_color', $data); // вот здесь я должен знать, что этот фильтр - "по цвету"      
А на самом деле все не так, повторюсь в очередной раз: я не знаю сколько групп фильтров (в виде выпадающих списков) будет выведено для конкретной (рассматриваемой) категории - может 2, а может и 10 - и имен их тоже заранее я ну никак не могу знать - их создает модератор через админку. Таким образом, в форме у меня вот что:

Код: Выделить всё

<?php foreach($filterGroups as $filterGroup): ?>
    <div class="form-label">
        <span><?php echo $filterGroup->name; ?></span>
    </div>
    <div class="form-item">
        <?php echo CHtml::activeDropDownList($goods, "filter[$filterGroup->id]", $filterGroup->getClientFilterItems()); ?>
    </div>
<?php endforeach; ?>
таким образом, в данном примере выше группы фильтров для данной (рассматриваемой) категории выводятся в цикле - их может быть сколь угодно и называться по-русски ($filterGroup->name) они могут как угодно - это название всего-навсего используется и выводится непосредственно перед самим выпадающим списком, а вот сами имена (<select name="">) выпадающих списков, которые мы и обсуждаем - уже дело другое - это то, что мы с вами и обсуждаем. В данном примере используется все тот же массив filter, ключами которого и являются группы фильтров, а в качестве значений выступают выбранные пользователем значения фильтров. Почему именно так я делаю? - Да потому что в контроллере я пишу вот так:

Код: Выделить всё

if(isset($_GET['Good']['filter'])) { // вот так мне проще и логичнее "поймать" этот массив, зная его имя
    // ...разные действия и условия для выборки из БД
}
Надеюсь, теперь понятно? Или все же я до сих пор что-то делаю не так?

Господа-коллеги, у меня еще один вопрос к вам: все же желаемого я пока добился, пускай и без ЧПУшности - это да, увы(( - хотя на многих интернет-магазинах-монстрах видел сплошные циферки при фильтрации, так что ЧПУшность - скорее, спорный момент. А вот на счет того, что говорил товарищ skeef (об ограниченной длине $_GET) - хотелось бы уточнить: она действительно ограничена 256ю символами, или все же размером в 5-6Кб, о чем говорят по приведенной ссылке. А еще важнее вот что: при автоматическом сабмите формы GET методом возможно ли как-то сделать так, чтобы выбранное в текущий момент значение не дописывалось в конец, а подменяло идентичное?

Последнее предложение-вопрос поясню на примере: допустим, пользователь хочет выбрать все товары черного цвета, изготовленные из дерева. Тогда, следуя моей логике URL примет вид:

mysite.com/category/1?Good[filter][1]=1&Good[filter][2]=2

, где
Good[filter][1] - группа фильтров с id=1, означающая "цвет" и в данном случае Good[filter][1]=1 - черный
Good[filter][2] - группа фильтров с id=2, означающая "материал" и в данном случае Good[filter][2]=2 - дерево

затем, допустим, пользователь там что-то себе передумал и захотел посмотреть все те же товары, опять же из дерева, но уже белого цвета, тогда URL после авто-сабмита формы станет таким:

mysite.com/category/1?Good[filter][1]=1&Good[filter][2]=2&Good[filter][1]=2

, где
Good[filter][1] - все та же группа фильтров с id=1, означающая "цвет" и в данном случае Good[filter][1]=2 - белый

в последнем URL'е выбор пользователя "дописался" в URL, а было бы очень круто, чтобы он заменил предыдущий выбор фильтра этой же группы (чтобы при имеющихся одинаковых именах переменных в GET использовалось только одно), то есть вот так:

mysite.com/category/1?Good[filter][1]=2&Good[filter][2]=2

Кому-нибудь приходилось сталкиваться с таким? - При таком подходе как раз-таки длину GET мы ни чуть не увеличивали бы, а просто заменяли нужные значения в адресе.

По-прежнему большое спасибо всем неравнодушным! Удачного и продуктивного дня ;)
Jonikru
Сообщения: 10
Зарегистрирован: 2011.02.07, 14:25

Re: Фильтрация значений методом GET

Сообщение Jonikru »

Здравствуйте!
Хочу подкинуть вам пару своих мыслей.

1. ЧПУ
Например, переформировать таблицу
filter_group: id (pk) | name (уникальное, индексируемое) | label.
Пример записи
id: 1
name: material
label: Материал
Поле name использовать при формировании имени для select, а в action осущетсвлять по нему поиск в filter_group.
Хотя, пока я не видел реальной пользы от чпу :-)

2. Изменение GET-параметров.
JS-костыль можно написать, который будет парсить параметры.

3. Размер GET-запроса
https://github.com/dreikanter/paradigm. ... -length.md
http://httpd.apache.org/docs/2.2/mod/co ... equestline
Ответить