Простой фильтр на PHP + MySQL. Нужна помощь...

ИА
На сайте с 12.02.2014
Offline
56
1202

Доброго времени суток!

Уважаемые PHPшники и в этом понимающие, подскажите решение задачи.

Делаю простенький фильтр по товарам и забуксовал на месте при встрече с одной проблемой. Нужна возможность выбора двух значений, в данном случае, двух вариантов цвета. Есть код HTML формы:

<form name="form" action="" method="post">
<table>
<tr>
<td>Цена от:</td>
<td><input type="text" name="price_start" /> рублей</td>
</tr>
<tr>
<td>Цена до:</td>
<td><input type="text" name="price_end" /> рублей</td>
</tr>
<tr>
<td>Wi-Fi:</td>
<td>
<input type="hidden" name="wifi" value="0" />
<input type="checkbox" name="wifi" />
</td>
</tr>
<tr>
<td>Bluetooth:</td>
<td>
<input type="hidden" name="bluetooth" value="0" />
<input type="checkbox" name="bluetooth" />
</td>
</tr>
<tr>
<td>Серый:</td>
<td>
<input type="hidden" name="gray" value="0" />
<input type="checkbox" name="gray" />
</td>
</tr>
<tr>
<td>Бежевый:</td>
<td>
<input type="hidden" name="beige" value="0" />
<input type="checkbox" name="beige" />
</td>
</tr>
<tr>
<td>Красный:</td>
<td>
<input type="hidden" name="red" value="0" />
<input type="checkbox" name="red" />
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="filter" value="Подобрать" />
</td>
</tr>
</table>
</form>

И сам ПХП код:

<?php
mysql_query ("SET NAMES utf8");

if($link)
$select_db = mysql_select_db($db);


function addWhere($where, $add, $and = true) {
if ($where) {
if ($and) $where .= " AND $add";
else $where .= " OR $add";
}
else $where = $add;
return $where;
}
if (!empty($_POST["filter"])) {
$where = "";
if ($_POST["price_start"]) $where = addWhere($where, "`price_rur` >= '".htmlspecialchars($_POST["price_start"]))."'";
if ($_POST["price_end"]) $where = addWhere($where, "`price_rur` <= '".htmlspecialchars($_POST["price_end"]))."'";
if ($_POST["wifi"]) $where = addWhere($where, "`wifi` = '1'");
if ($_POST["bluetooth"]) $where = addWhere($where, "`bluetooth` = '1'");
if ($_POST["gray"]) $where = addWhere($where, "`color` = 'серый'");
if ($_POST["beige"]) $where = addWhere($where, "`color` = 'бежевый'");
if ($_POST["red"]) $where = addWhere($where, "`color` = 'красный'");
$sql = "SELECT * FROM `tovar`";
if ($where) $sql .= " WHERE $where";
echo $sql;
$query = mysql_query($sql);
if(!mysql_num_rows($query))
echo "В таблице нет данных!";
else
{
while($row = mysql_fetch_assoc($query))
{
echo '<ul>
<strong><li>'.$row['name'].'</li>
</ul>';
}
}
}
?>

Вид MySQL таблицы

name -------- color ------- wifi --- bluetooth --- price

Товар 1 ---- Бежевый ---- 1 ----------- 1

Товар 2 ---- Серый ------- 1 ----------- 0

Товар 3 ---- Красный ---- 0 ----------- 1

Так вот, все, вроде ОК, но это на первый взгляд. Т.е., если отмечаем чекбоксы разных категорий, например, "Wi-Fi" и "серый", то получаем рабочий запрос вида:

SELECT * FROM `table` WHERE `wifi` = '1' AND `color` = 'серый';

Но, если отметим 2 варианта цвета, то получаем такой вот нерабочий запрос:

SELECT * FROM `tovar` WHERE `color` = 'серый' AND `color` = 'бежевый'

И,собственно, вопрос: как можно выйти из этой ситуации и можно ли это исправить в рабочий вариант или же надо что-то другое думать?

Как я понимаю, рабочий запрос должен быть такого вида

$sql = "SELECT * FROM `tovar` WHERE `wifi` = '1' AND (color='серый' OR color='бежевый')";

или такой

$sql = "SELECT * FROM `tovar` WHERE color IN ('серый','бежевый') AND `wifi` = '1'";

И сам внешний вид фильтра

P.S. Сильно не пинайте, пока что только начинаю изучать ПХП и, как обычно, сразу на практике.

M
На сайте с 04.12.2013
Offline
181
#1

Если одинаковые поля, OR, а если разные, AND.

---------- Добавлено 18.02.2019 в 13:39 ----------

IN – это то же самое, что и (множественный) OR. В случае OR не забываем, что у него приоритет ниже, чем AND, поэтому берем в скобки. Тут все верно.

---------- Добавлено 18.02.2019 в 13:44 ----------

`wifi` = '1'
??? Может, все-таки `category`=1 или `category`='wifi' ?

---------- Добавлено 18.02.2019 в 13:47 ----------

Когда уже вы перестанете использовать расширение mysql? Используйте хотя бы mysqli.

---------- Добавлено 18.02.2019 в 13:51 ----------

Учитесь отделять код запросов от шаблонов, не делать полноценный вывод в ответ на POST и т.п.

Чтобы на этом форуме нормально отображался код, используйте просто CODE.

Хостинг FOZZY (http://fozzy.com.ru) / Выслушаю предложения на домены (http://u75.ru/domains-for-sale) / Домены и скрипт для коротких ссылок (http://u75.ru/domains-for-shortcuts)
ИА
На сайте с 12.02.2014
Offline
56
#2
miketomlin:
Если одинаковые поля, OR, а если разные, AND.

---------- Добавлено 18.02.2019 в 13:39 ----------

IN – это то же самое, что и (множественный) OR. В случае OR не забываем, что у него приоритет ниже, чем AND, поэтому берем в скобки. Тут все верно.

Так в том-то и вопрос, что в данном коде ставится AND и при разных полях, и при одинаковых. Это и надо как-то исправить, а своих мозгов пока не хватает...

miketomlin:
??? Может, все-таки `category`=1 или `category`='wifi' ?

А чем `wifi` = '1' не устраивает? Тоже самое, что и `category`='wifi'. 1 - есть Wi-Fi, 0 - нет.

miketomlin:
Чтобы на этом форуме нормально отображался код, используйте просто CODE.

Поправил.

M
На сайте с 04.12.2013
Offline
181
#3
Илья Артурович:
А чем `wifi` = '1' не устраивает? Тоже самое, что и `category`='wifi'. 1 - есть Wi-Fi, 0 - нет.

Я думал, это категория. Нужно лучше объяснять, а то пока, судя по форме, это у вас цвет такой :) Чтобы было понятнее, что это отдельный признак, формализуйте описание исходных данных, тогда уже можно будет отвечать на вопросы вроде

Так в том-то и вопрос, что в данном коде ставится AND и при разных полях, и при одинаковых. Это и надо как-то исправить, а своих мозгов пока не хватает...



---------- Добавлено 18.02.2019 в 15:31 ----------

P.S. Если вы таким экзотическим образом пытались изобразить принадлежность одновременно к нескольким категориям, почитайте про связывание «многие-ко-многим» ;)

S
На сайте с 30.09.2016
Offline
459
#4
Илья Артурович:
Это и надо как-то исправить

Правьте код, вводите для выбора цвета свою функцию, дополнительно к функции addWhere.

Отпилю лишнее, прикручу нужное, выправлю кривое. Вытравлю вредителей.
PN
На сайте с 22.08.2012
Offline
103
#5
Илья Артурович:
Доброго времени суток!

Уважаемые PHPшники и в этом понимающие, подскажите решение задачи.

Делаю простенький фильтр по товарам и забуксовал на месте при встрече с одной проблемой. Нужна возможность выбора двух значений, в данном случае, двух вариантов цвета. Есть код HTML формы:

<form name="form" action="" method="post">

<table>
<tr>
<td>Цена от:</td>
<td><input type="text" name="price_start" /> рублей</td>
</tr>
<tr>
<td>Цена до:</td>
<td><input type="text" name="price_end" /> рублей</td>
</tr>
<tr>
<td>Wi-Fi:</td>
<td>
<input type="hidden" name="wifi" value="0" />
<input type="checkbox" name="wifi" />
</td>
</tr>
<tr>
<td>Bluetooth:</td>
<td>
<input type="hidden" name="bluetooth" value="0" />
<input type="checkbox" name="bluetooth" />
</td>
</tr>
<tr>
<td>Серый:</td>
<td>
<input type="hidden" name="gray" value="0" />
<input type="checkbox" name="gray" />
</td>
</tr>
<tr>
<td>Бежевый:</td>
<td>
<input type="hidden" name="beige" value="0" />
<input type="checkbox" name="beige" />
</td>
</tr>
<tr>
<td>Красный:</td>
<td>
<input type="hidden" name="red" value="0" />
<input type="checkbox" name="red" />
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="filter" value="Подобрать" />
</td>
</tr>
</table>
</form>


И сам ПХП код:

<?php

mysql_query ("SET NAMES utf8");

if($link)
$select_db = mysql_select_db($db);


function addWhere($where, $add, $and = true) {
if ($where) {
if ($and) $where .= " AND $add";
else $where .= " OR $add";
}
else $where = $add;
return $where;
}
if (!empty($_POST["filter"])) {
$where = "";
if ($_POST["price_start"]) $where = addWhere($where, "`price_rur` >= '".htmlspecialchars($_POST["price_start"]))."'";
if ($_POST["price_end"]) $where = addWhere($where, "`price_rur` <= '".htmlspecialchars($_POST["price_end"]))."'";
if ($_POST["wifi"]) $where = addWhere($where, "`wifi` = '1'");
if ($_POST["bluetooth"]) $where = addWhere($where, "`bluetooth` = '1'");
if ($_POST["gray"]) $where = addWhere($where, "`color` = 'серый'");
if ($_POST["beige"]) $where = addWhere($where, "`color` = 'бежевый'");
if ($_POST["red"]) $where = addWhere($where, "`color` = 'красный'");
$sql = "SELECT * FROM `tovar`";
if ($where) $sql .= " WHERE $where";
echo $sql;
$query = mysql_query($sql);
if(!mysql_num_rows($query))
echo "В таблице нет данных!";
else
{
while($row = mysql_fetch_assoc($query))
{
echo '<ul>
<strong><li>'.$row['name'].'</li>
</ul>';
}
}
}
?>


Вид MySQL таблицы

name -------- color ------- wifi --- bluetooth --- price

Товар 1 ---- Бежевый ---- 1 ----------- 1
Товар 2 ---- Серый ------- 1 ----------- 0
Товар 3 ---- Красный ---- 0 ----------- 1

Так вот, все, вроде ОК, но это на первый взгляд. Т.е., если отмечаем чекбоксы разных категорий, например, "Wi-Fi" и "серый", то получаем рабочий запрос вида:

SELECT * FROM `table` WHERE `wifi` = '1' AND `color` = 'серый';

Но, если отметим 2 варианта цвета, то получаем такой вот нерабочий запрос:

SELECT * FROM `tovar` WHERE `color` = 'серый' AND `color` = 'бежевый'

И,собственно, вопрос: как можно выйти из этой ситуации и можно ли это исправить в рабочий вариант или же надо что-то другое думать?

Как я понимаю, рабочий запрос должен быть такого вида

$sql = "SELECT * FROM `tovar` WHERE `wifi` = '1' AND (color='серый' OR color='бежевый')";

или такой

$sql = "SELECT * FROM `tovar` WHERE color IN ('серый','бежевый') AND `wifi` = '1'";

И сам внешний вид фильтра



P.S. Сильно не пинайте, пока что только начинаю изучать ПХП и, как обычно, сразу на практике.

1. Используйте PDO.

2. Забудьте про if ($and) $where .= " AND $add";, используйте функциональное программирование.

3. Как с вами связаться (скайп/телега)?

Мой совет помог? Не скупись! Bitcoin 1Lseddet1o1B6odgXQHbGaWGwRkt1Db8Ef Ethereum 0x450f1a17461e25194B7F9226cDEe70173F39e1e1
S
На сайте с 30.09.2016
Offline
459
#6
proksey-net:
используйте функциональное программирование

Не забивай человеку голову всяким хламом.

ИА
На сайте с 12.02.2014
Offline
56
#7
Sitealert:
Правьте код, вводите для выбора цвета свою функцию, дополнительно к функции addWhere.

Спасибо за направление. Отчасти помогло, но еще пока не совсем.

Добился чтобы на выходе был запрос вида

$sql = "SELECT * FROM `tovar` WHERE `wifi` = '1' (color='серый' OR color='бежевый')";

Теперь думаю как вот сюда AND вставить... И проблема будет решена.

$sql = "SELECT * FROM `tovar` WHERE `wifi` = '1' AND (color='серый' OR color='бежевый')";

---------- Добавлено 18.02.2019 в 21:06 ----------

proksey-net:
1. Используйте PDO.
2. Забудьте про if ($and) $where .= " AND $add";, используйте функциональное программирование.

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

M
На сайте с 04.12.2013
Offline
181
#8
Илья Артурович:
Теперь думаю как вот сюда AND вставить...

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


if (count($reqlogic)) {
$req.=' AND ('.implode(' связка ',$reqlogic).')';
}

if (count($reqcolors)) {
$req.=' AND `color` IN('.implode(',',$reqcolors).')';
}
И проблема будет решена.

Угу. Очередной костыль увидит свет :D

---------- Добавлено 19.02.2019 в 08:34 ----------

P.S. Для тех, кто в танке...

Данные берутся из «словарей» в БД:


$logic=[ 'wifi'=>'Wi-Fi',
'bluetooth'=>'Bluetooth',
];

$colors=[ 1=>'Серый',
2=>'Бежевый',
3=>'Красный',
];

Фактические данные не хранятся в коде. Формы тоже строятся на основе данных из БД. По этим же данным можно фильтровать и ввод, например:


$reqlogic=[];
foreach ($logic as $key=>$val) {
if (!empty($_POST[$key])) {
$reqlogic[]='`'.$key.'`';
}
}

$reqcolors=[];
foreach ($colors as $key=>$val) {
if (!empty($_POST['c'.$key])) {
$reqcolors[]=$key;
}
}

Чтобы не возиться с кавычками у элементов $reqlogic, можно потом сделать так:


if (count($reqlogic)) {
$req.=' AND (`'.implode('` связка `',$reqlogic).'`)';
}
ИА
На сайте с 12.02.2014
Offline
56
#9

Всем большое спасибо за помощь. В итоге разобрался я таки с ним.

В итоге цвет сделал через массив


<tr>
<td>Bluetooth:</td>
<td>
<input type="checkbox" name="bluetooth" />
</td>
</tr>
<tr>
<td>Серый:</td>
<td>
<input type="checkbox" name="color[]" value="серый" />
</td>
</tr>
<tr>
<td>Красный:</td>
<td>
<input type="checkbox" name="color[]" value="красный" />
</td>
</tr>

И дальше через implode

if (isset($_POST["color"])) { $where = addWhere($where, "`color` IN ("."'".htmlspecialchars(implode("','", $_POST["color"]))."'".")");}

if (isset($_POST["bluetooth"])) { $where = addWhere($where, "`bluetooth` = '1'");}

И все заработало!

M
На сайте с 04.12.2013
Offline
181
#10

Ну т.е. танк оказался непрошибаемым – все захардкодили :)

Илья Артурович:
В итоге цвет сделал через массив

Это на большого любителя, под конкретный язык и т.д. А показанный мной вариант с разными именами контроллов вполне стандартный.

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий