2016-03-12 4 views
3

Там 3 различных фильтров: books, authors и stores (select списки), и я могу использовать их все сразу или только один или два из них, поэтому я использую UNION, чтобы объединить все запросыСвязывание нескольких массивов в п.д.о.

require('database.php'); 

if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
} 

$query = ""; 

if(!empty($books_ids)) 
{ 
    $books_ids_in = implode(',', array_fill(0, count($books_ids), '?')); 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (". $books_ids_in .") 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 

if(!empty($authors_ids)) 
{ 
    $authors_ids_in = implode(',', array_fill(0, count($authors_ids), '?')); 

    if (!empty($query)) { 
     $query .= " UNION "; 
    } 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'author' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (
      SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (". $authors_ids_in .") 
      ) 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 

if(!empty($stores_ids)) 
{ 
    $stores_ids_in = implode(',', array_fill(0, count($stores_ids), '?')); 

    if (!empty($query)) { 
     $query .= " UNION "; 
    } 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'store' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (
      SELECT DISTINCT book_id FROM books_stores WHERE store_id IN (". $stores_ids_in .") 
      ) 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 


if(!empty($query)) { 

    $stmt = $conn->prepare($query); 

    if(!empty($books_ids)) 
    { 
     foreach ($books_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    if(!empty($authors_ids)) 
    { 
     foreach ($authors_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    if(!empty($stores_ids)) 
    { 
     foreach ($stores_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    $stmt->execute(); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

$conn = null; 

код прекрасно работает, когда я использую только один фильтр, но когда я пытаюсь использовать 2 или более, я получаю ошибку

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens' in C:\xampp\htdocs\bookstore\filter.php:123 Stack trace: #0 C:\xampp\htdocs\bookstore\filter.php(123): PDOStatement->execute() #1 {main} thrown in C:\xampp\htdocs\bookstore\filter.php on line 123

Я думаю, что-то не так с использованием bindValue, но я не знаю, как это исправить?

UPD var_dump($query) (3 книги и 2 авторы выбрали)
string(1097) "SELECT b.id, b. имени , b. года , GROUP_CONCAT(DISTINCT a. имени ) AS author_names, GROUP_CONCAT(DISTINCT s. имени ) AS store_names, 'book' as param FROM books AS b LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id LEFT JOIN authors AS a ON a.id = b_a.author_id LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id LEFT JOIN stores AS s ON s.id = b_s.store_id WHERE b.id IN (?,?,?) GROUP BY b.id ORDER BY b.id UNION SELECT b.id, b. имени , b. года , GROUP_CONCAT(DISTINCT a. имени ) AS author_names, GROUP_CONCAT(DISTINCT s. имени ) AS store_names, 'author' as param FROM books AS b LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id LEFT JOIN authors AS a ON a.id = b_a.author_id LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id LEFT JOIN stores AS s ON s.id = b_s.store_id WHERE b.id IN (SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (?,?)) GROUP BY b.id ORDER BY b.id" 01201

+0

Ошибка означает, что у вас меньше/больше значений привязки, чем ожидалось в запросе. Я бы выделил количество циклов bindValue, чтобы найти виновника. –

+0

Нет, количество аргументов такое же, как в запросе, я уже проверил. Я выбрал 3 книги и 2 автора и в моем запросе 'WHERE b.id IN (?,?,?)' И 'WHERE author_id IN (?,?))' – Heidel

+0

var_dump ($ query); и показать результат, пожалуйста, –

ответ

3

Есть проблемы с вашим кодом для построения динамического запрос. При построении динамического запроса вам нужно отделить те части запроса, которые являются статическими из динамических.

Вы можете видеть, что следующий код является статическим.

$query = "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id "; 

А также

" GROUP BY b.id 
    ORDER BY b.id"; 

Остальная часть кода является динамическим. При фильтрации записей используется предложение WHERE, и операторы AND & OR используются для фильтрации записей на основе более чем одного условия. Оператор AND отображает запись, если верно и первое условие, и второе условие. Оператор ИЛИ отображает запись, если либо первое условие ИЛИ второе условие истинно. поэтому для первого условия WHERE используется, но после этого И или ИЛИ должны быть использованы (с помощью или в вашем примере)

// Static code 
sql = "SELECT * FROM `table`" 
// Set initial condition to WHERE  
clause = "WHERE";  
if(!empty(filter)){ 
    Add clause to sql 
    Add condition to sql 
    change clause to OR or AND as required 
} 
Repeat for each filter 
Note the filter is not changed until a filter is not empty and remains changed once changed. 
The remaining static code is added after all the filters have been handled 

Чтобы разрешить различные фильтры, которые будут применяться вы можете использовать flag.

$flag = 0; 
if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
    $flag += 1; 

} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
    $flag += 10; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
    $flag += 100; 
} 

Использовать «ленивую» привязку, когда возможно - передача данных в исполнение значительно сократит ваш код. См. PDO info Для выполнения этого требуется объединение массива. Используя оператор switch с флагом, вы объединяете требуемые массивы.

switch ($flag) { 
    case 1: 
     $param_array = $books_ids; 
     break; 
    case 10: 
     $param_array = $authors_ids; 
     break; 
    case 100: 
     $param_array = $stores_ids; 
     break; 
    case 11://books & authors 
     $param_array = array_merge($books_ids, $authors_ids); 
     break; 
    case 101://books & stores 
     $param_array = array_merge($books_ids, $stores_ids); 
     break; 
    case 110://authors & stores 
     $param_array = array_merge($authors_ids, $stores_ids); 
     break; 
    case 111://books & authors & stores 
     $param_array = array_merge(array_merge($books_ids,$authors_ids),$stores_ids); 
     break; 

} 

if(!empty($query)) { 
    $stmt = $conn->prepare($query); 
    $stmt->execute($param_array); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

Следующий код использует вышеуказанные пункты. Я повторил несколько строк, чтобы указать результаты, которые могут быть удалены после завершения тестирования. Кроме того, для тестирования был прокомментирован некоторый код.

//Set flag 
$flag = 0; 
if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
    $flag += 1; 
} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
    $flag += 10; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
    $flag += 100; 
} 
echo $flag. " <BR>";//Remove after testing 

//Basic SQL statement 
$query = "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id "; 
// Set initial condition to WHERE  
$clause = "WHERE";  

if(!empty($books_ids)) 
{ 
    $books_ids_in = implode(',', array_fill(0, count($books_ids), '?')); 
    $query .= $clause; 
    $query .= " b.id IN (". $books_ids_in .")"; 
    // Set condition to OR for additional condition 
    $clause = " OR "; 
} 
if(!empty($authors_ids)) 
{ 
    $authors_ids_in = implode(',', array_fill(0, count($authors_ids), '?')); 

    /* This part commented out as I don't see relevance 
     if (!empty($query)) { 
     $query .= " UNION "; 
    } 
    */ 
    $query .= $clause; 
    $query .= " b.id IN (
      SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (". $authors_ids_in .") 
      )"; 
    // Set condition to OR for additional condition  
    $clause = " OR ";  

} 


if(!empty($stores_ids)) 
{ 
    $stores_ids_in = implode(',', array_fill(0, count($stores_ids), '?')); 

    /* if (!empty($query)) { 
     $query .= " UNION "; 
    } 
    */ 
    $query .= $clause; 
    $query .= " b.id IN (
      SELECT DISTINCT book_id FROM books_stores WHERE store_id IN (". $stores_ids_in .") 
      )"; 
    $clause = " OR "; 
} 

//Add GROUP & ORDER 
    $query .= " GROUP BY b.id 
    ORDER BY b.id"; 
echo $query; //Remove after testing 
//building $param_array 
switch ($flag) { 
    case 1: 
     $param_array = $books_ids; 
     break; 
    case 10: 
     $param_array = $authors_ids; 
     break; 
    case 100: 
     $param_array = $stores_ids; 
     break; 
    case 11://books & authors 
     $param_array = array_merge($books_ids, $authors_ids); 
     break; 
    case 101://books & stores 
     $param_array = array_merge($books_ids, $stores_ids); 
     break; 
    case 110://authors & stores 
     $param_array = array_merge($authors_ids, $stores_ids); 
     break; 
    case 111://books & authors & stores 
     $param_array = array_merge(array_merge($books_ids,$authors_ids),$stores_ids); 
     break; 

} 
echo "<br>"; 
print_r($param_array);// remove after testing 
/* 
if(!empty($query)) { 
    $stmt = $conn->prepare($query); 
    $stmt->execute($param_array); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

$conn = null; 
+0

Проблема в том, что я могу использовать все 3 фильтра или только 1 или 2 из них, поэтому у меня могут быть массивы '$ books_ids',' $ authors_ids' и '$ stores_ids', или только 1 или 2 из них. В этом случае у меня есть такие ошибки, как 'Примечание: неопределенная переменная: books_ids в C: \ xampp \ htdocs \ bookstore \ filter.php в строке 130 Предупреждение: array_merge(): Аргумент # 1 не является массивом в C: \ xampp \ htdocs \ bookstore \ filter.php в строке 130' – Heidel

+0

@Heidel добавили код для ответа. –

+0

@Heidel Я обновил ответ –

0

Не используйте одинаковые $ k; использовать переменную и увеличивать ее с каждой привязкой; См. Ниже

$bindingIndex = 0; 
if(!empty($books_ids)) 
{ 
    foreach ($books_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
} 

if(!empty($authors_ids)) 
{ 
    foreach ($authors_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
} 

if(!empty($stores_ids)) 
{ 
    foreach ($stores_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
} 
Смежные вопросы