Конструктор запросов Laravel: DB::raw с инструкцией IF дает неправильные результаты

#mysql #laravel #if-statement

#mysql #laravel #if-оператор #if-statement

Вопрос:

У меня есть запрос, который работает в mysql, но при выполнении его в laravel я получаю неправильные результаты. обновление: версия Laravel — 4.2.

Запрос извлекает все книги, но если у книги есть «visibility_school» (логическое значение), то школа текущего пользователя должна соответствовать школе автора книги — поэтому я использовал для этого инструкцию IF.

Этот запрос mysql работает: (смотрите WHERE IF Строку, где я жестко закодировал значение 2 ( schools.id = 2 ) для сравнения для тестирования, обычно оно динамическое)

 SELECT book.*, user.name AS name, user.school_class AS schoolClass, schools.name as 
       schoolName, count(DISTINCT book.id) AS overallCount, 
       AVG(book_rating.mark) AS rating 
FROM book
LEFT JOIN user ON book.user_id = user.id
LEFT JOIN book_tag ON book.id = book_tag.book_id
LEFT JOIN book_rating ON book.id = book_rating.book_id
LEFT JOIN schools ON schools.id = user.school_id
WHERE IF(book.visibility_school = 1, IF(schools.id = 2, 1, 0), 1) = 1       
AND book.published = 1                      
AND book.deleted = 0                 
GROUP BY book.id
  

При выполнении этого в построителе запросов Laravel мой код выглядит следующим образом:

 $books = DB::table('book')
   ->select(DB::raw("book.*, user.name AS name, user.school_class AS schoolClass, 
               schools.name as schoolName,count(DISTINCT book.id) AS overallCount, 
               AVG(book_rating.mark) AS rating"))
->leftJoin('user', 'book.user_id', '=', 'user.id') 
->leftJoin('book_tag', 'book.id', '=', 'book_tag.book_id')    
->leftJoin('book_rating', 'book.id', '=', 'book_rating.book_id')    
->leftJoin('schools', 'schools.id', '=', 'user.school_id')  
->where(DB::raw("IF(book.visibility_school = 1, IF(schools.id = '?', 1, 0), 1) = 1"),$currentUserSchoolId)
->where('book.published',Book::STATUS_PUBLISHED)
->where('book.deleted',0) 
->groupBy('book.id');
  

Запрос mysql правильно возвращает 5 опубликованных книг, и если я установлю schools.id = 3 , будет включена дополнительная книга, видимая только членам определенного школьного идентификатора.

Однако код laravel не возвращает книг, за двумя исключениями:

  1. Если я установлю schools.id = 0 , я получу 1 результат, одну книгу, видимую только членам определенной школы (у которой должен быть идентификатор 3).

  2. Если я установлю schools.id = 1 — я получу 5 опубликованных книг. Если я задаю какие-либо другие идентификаторы, я не получаю результатов.

Нет никакой разницы, если я помещу $currentUserSchoolId внутрь массива [$currentUserSchoolId] . Также Book::STATUS_PUBLISHED свойство равно 1, так что там нет разницы с версией кода mysql.

Таким образом, laravel, похоже, оценивает строку « where if » как false, даже если visibility_school значение не равно 1.

——— Обновить ———

После предложения я изменил where if строку (а также добавил к ней: AND schools.id <> 0 ):

 ->where(DB::raw("book.visibility_school <> 1 OR schools.id = '?' AND schools.id <> 0"), 
      $currentUserSchoolId)
  

Теперь я всегда получаю опубликованные книги, так что это хорошо, но я все еще не получаю книгу, видимую для соответствующего школьного идентификатора. Если я установил schools.id = 3 , я должен получить его, и он все еще работает в mysql. Но если я установлю для него значение 0 (и удалю И schools.id <> 0) Я тоже получаю эту книгу — поэтому laravel считает, что идентификатор школы по какой-то причине равен 0. Итак, это моя текущая проблема.

Комментарии:

1. Я думаю, что ваше состояние действительно WHERE (book.visibility_school != 1 OR schools.id = 2) AND .. .

2. Я предполагаю, что это лучший способ написать это, но я думаю, что логика та же. Когда я добавил это в laravel: DB::raw(«book.visibility_school <> 1 ИЛИ schools.id = ‘?’ И schools.id <> 0» — опубликованные книги отображаются так, как должны, поэтому, возможно, laravel не понравился синтаксис, который я использовал. Но я не получаю книгу «visibility_school», когда я помещаю соответствующий идентификатор школы 3. Laravel, похоже, думает, что у этой книги есть идентификатор школы 0 (потому что он совпадает, когда я помещаю school.id = 0 (и удалите дополнительное предложение, которое я добавил «И schools.id <> 0»). В mysql запрос все же работает.

Ответ №1:

При использовании привязки параметров вам не нужно заключать кавычки вокруг вопросительного знака, так ? и не '?' .

 DB::raw("IF(book.visibility_school = 1, IF(schools.id = ?, 1, 0), 1) = 1"),$currentUserSchoolId)
  

Однако, как предлагали другие, ваши проверки могут быть упрощены с помощью обычных операций и / или

Комментарии:

1. Если я удалю кавычки вокруг ? , я получу «SQLSTATE [HY093]: недопустимый номер параметра «. Я использую laravel 4.2, возможно, для этой версии требуется цитата (?)

Ответ №2:

Есть ли какая-либо причина, по которой вы не пользуетесь ORM, предоставляемым Laravel?

Со всеми отношениями, хорошо установленными в ваших моделях, вы должны получить более или менее похожий результат со следующим кодом:

MyController.php

 $currentUserId = Auth::id();
$books = Book::with(['tag', 'rating', 'user', 'user.school' => function ($query) {
        $query->selectRaw('name as schoolName')->addSelect('id');
    }])
    ->whereHas('user', function ($query) use ($currentUserId) {
        $query->where('id', $currentUserId);
    })
    ->where('published', Book::STATUS_PUBLISHED)
    ->where('visibility_school', true)
    ->where('deleted', false)
    ->get();
}
  

Комментарии:

1. Я не хочу использовать его слишком часто для сложных запросов, потому что это медленнее. Запрос также должен возвращать все результаты book.visibility_school = 0, но если это 1, то школа автора книги должна соответствовать школе текущего пользователя.