Checbox-фильтр по нескольким категориям (MySQL, PHP, Ajax)

275
10 января 2018, 19:31

Существует таблица Mysql с различными устройствами, которые необходимо отсортировать по 3м категориям с помощью checkbox.

Сама таблица:

CREATE TABLE IF NOT EXISTS `test_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`phone_name` varchar(255) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`samsung` tinyint(1) DEFAULT NULL,
`apple` tinyint(1) DEFAULT NULL,
`nokia` tinyint(1) DEFAULT NULL,
`touchscreen` tinyint(1) DEFAULT NULL,
`qwerty` tinyint(1) DEFAULT NULL,
`classic` tinyint(1) DEFAULT NULL,
`single_core` tinyint(1) DEFAULT NULL,
`dual_core` tinyint(1) DEFAULT NULL,
`quad_core` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `test_table` (`id`, `phone_name`, `price`, `samsung`, `apple`, 
`nokia`, `touchscreen`, `qwerty`, `classic`, `single_core`, `dual_core`, `quad_core`)     
VALUES
(1, 'Samsung Galaxy S4', 470, 1, 0, 0, 1, 0, 0, 0, 0, 1),
(2, 'Samsung Chat', 220, 1, 0, 0, 1, 1, 0, 1, 0, 0),
(3, 'Iphone 4', 380, 0, 1, 0, 1, 0, 0, 0, 1, 0),
(4, 'Iphone 5', 550, 0, 1, 0, 1, 0, 0, 0, 0, 1),
(5, 'Nokia Lumia 520', 150, 0, 0, 1, 1, 0, 0, 0, 1, 0),
(6, 'Nokia E72', 250, 0, 0, 1, 0, 1, 0, 1, 0, 0);

index.html

<div id="filter">
  <h2>Filter options</h2>
  <div><input type="checkbox" id="samsung" name="samsung"> <label for="samsung">samsung</label></div>
  <div><input type="checkbox" id="apple" name="apple">   <label for="apple">apple</label></div>
  <div><input type="checkbox" id="nokia" name="nokia">   <label for="nokia">nokia</label></div>
  <div><input type="checkbox" id="touchscreen" name="touchscreen"> <label for="touchscreen">touchscreen</label></div>
  <div><input type="checkbox" id="qwerty" name="qwerty">      <label for="qwerty">qwerty</label></div>
  <div><input type="checkbox" id="classic" name="classic">     <label for="classic">classic</label></div>
  <div><input type="checkbox" id="single_core" name="single_core">        <label for="single_core">single_core</label></div>
  <div><input type="checkbox" id="dual_core" name="dual_core">      <label for="dual_core">dual_core</label></div>
  <div><input type="checkbox" id="quad_core" name="quad_core">      <label for="quad_core">quad_core</label></div>
    </div>
<script src="jquery-3.2.1.min.js"></script>
    <script src="common.js"></script>

common.js

function getEmployeeFilterOptions(){
var opts = [];
$checkboxes.each(function(){
if(this.checked){
opts.push(this.name);
}
});
return opts;
}
function updateEmployees(opts){
$.ajax({
    type: "POST",
    url: "submit.php",
    dataType : 'json',
    cache: false,
    data: {filterOpts: opts},
    success: function(records){
        $('#mobile-phones tbody').html(makeTable(records));
    },
    error: function(data) {
        console.log("ERROR");
    }
    });
}
var $checkboxes = $("input:checkbox");
$checkboxes.on("change", function(){
var opts = getEmployeeFilterOptions();
updateEmployees(opts);
});
updateEmployees();

submit.php

<?php 
$pdo = new PDO('mysql:host=localhost;dbname=test_database', 'root', '');
$select = 'SELECT id, phone_name, price';
$from = ' FROM test_table';
$where = ' WHERE TRUE';
$opts = isset($_POST['filterOpts'])? $_POST['filterOpts'] : array('');
if (in_array("samsung", $opts)){
$where .= " OR samsung = 1";
} 
if (in_array("apple", $opts)){
$where .= " OR apple = 1";
} 
if (in_array("nokia", $opts)){
$where .= " OR nokia = 1";
} 
if (in_array("touchscreen", $opts)){
$where .= " OR touchscreen = 1";
} 
if (in_array("qwerty", $opts)){
$where .= " OR qwerty = 1";
} 
if (in_array("classic", $opts)){
$where .= " OR classic = 1";
} 
if (in_array("single_core", $opts)){
$where .= " OR single_core = 1";
}   
if (in_array("dual_core", $opts)){
$where .= " OR dual_core = 1";
}   
if (in_array("quad_core", $opts)){
$where .= " OR quad_core = 1";
}   
$sql = $select . $from . $where;
$statement = $pdo->prepare($sql);
$statement->execute();
$results = $statement->fetchAll(PDO::FETCH_ASSOC);
$json = json_encode($results);
echo($json);
?>

Проблема в том, что использовать AND нельзя, т.к. при выборе нескольких элементов ajax возвращает пустой массив, а при использовании OR в любом случае возвращается массив со всеми 6-ю телефонами из таблицы. Есть ли рациональный способ фильтрации по 3-м категориям?

Answer 1

Стандартные варианты отбора из списка по набору фильтров таковы:

1) Для любой СУБД

SELECT select_fieldset
FROM tables
WHERE (property, value) IN (список пар свойство-значение)
GROUP BY /* id */ groupby_fieldset
HAVING COUNT(*) = (количество пар свойство-значение)

2) Для MySQL это можно реализовать и в такой форме:

SELECT fieldset
FROM tables
GROUP BY id
HAVING SUM((property, value) IN (список пар свойство-значение)) = (количество пар свойство-значение)

Само собой оба способа предполагают хранение данных, нормализованное в EAV. Для денормализованного хранения (в показанной структуре) придётся извращаться, собирая свойства из отдельных полей в одно значение, которое затем стандартно проверять на соответствие маски (value AND mask = mask). Или писать какое-то многоэтажное безобразие типа

(field1 AND check1) + ... + (fieldN AND checkN) = count(check=1)
READ ALSO
Пересечение временных интервалов

Пересечение временных интервалов

Есть массив времени:

213
Проверка isset($_FILES[filename]) не работает

Проверка isset($_FILES[filename]) не работает

ЗдравствуйтеВ форме есть поле <input type="file" name="filename" /> При отправке формы идёт проверка

235
JS отдача файла клиенту

JS отдача файла клиенту

Есть вопросСуть - на сервере Symfony, контроллер генерирует

180
Подключение к сайту по сокетам

Подключение к сайту по сокетам

Пытаюсь разобраться с websocket'ами на phpЕсть веб хостинг (сайт), нужно подключиться к нему по сокетам

189