【问题标题】:Laravel query builder: DB::raw with IF statement gives wrong resultsLaravel 查询生成器:带有 IF 语句的 DB::raw 给出错误的结果
【发布时间】:2019-04-03 18:55:17
【问题描述】:

我有一个在 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,则包含仅对特定学校 ID 的成员可见的额外书籍。

然而,laravel 代码不返回任何书籍,但有两个例外:

  1. 如果我设置schools.id = 0,我会得到 1 个结果,一本书只对某个学校的成员可见(应该是 id 3)。

  2. 如果我设置schools.id = 1 - 我会得到 5 本书。如果我设置任何其他 ID,我不会得到任何结果。

如果我将$currentUserSchoolId 放在数组[$currentUserSchoolId] 中,则没有区别。 Book::STATUS_PUBLISHED属性也等于1,所以那里的mysql代码版本没有区别。

因此,即使 visibility_school 不是 1,laravel 似乎也会将“where if”行评估为 false

--------- 更新 ---------

根据建议,我更改了where if 行(并添加到该行:AND schools.id <> 0):

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

现在我总是能看到已出版的书籍,这很好,但我仍然无法让匹配的学校 ID 看到这本书。如果我设置schools.id = 3 我应该得到它,它仍然可以在 mysql 中使用。但是如果我将它设置为 0(并删除 ANDschools.id 0),我也会得到那本书 - 所以 laravel 认为学校 id 出于某种原因是 0。这就是我目前的问题。

【问题讨论】:

  • 我觉得你的情况真的是WHERE (book.visibility_school != 1 OR schools.id = 2) AND ..
  • 我想这是一个更好的写法,但我认为逻辑是一样的。当我将此添加到 laravel 时: DB::raw("book.visibility_school 1 OR school.id = '?' AND school.id 0" - 已出版的书籍按应有的方式显示,所以也许 laravel 没有t 喜欢我使用的语法。但是当我输入匹配的学校 id 3 时,我没有得到“visibility_school”书。Laravel 似乎认为那本书的学校 id 为 0(因为当我输入 school.id = 0 时它匹配(并删除我添加的额外子句“AND school.id 0”)。在 mysql 中,查询虽然有效。

标签: mysql laravel if-statement


【解决方案1】:

使用参数绑定时,您不需要在问号两边加上引号,所以? 而不是'?'

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

但是,正如其他人建议的那样,您可以使用正常和/或操作来简化检查

【讨论】:

  • 如果我删除周围的报价? ,我得到 "SQLSTATE[HY093]: Invalid parameter number" 。我用的是 laravel 4.2,也许那个版本需要引号(?)
【解决方案2】:

你有什么理由不利用the ORM provided by Laravel

在您的models 中设置好所有relationships 后,您应该使用以下代码获得或多或少相似的结果:

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();
}

【讨论】:

  • 我不想过多地使用它来处理复杂的查询,因为它比较慢。该查询还应返回所有结果均为 book.visibility_school = 0,但如果为 1,则图书作者的学校应与当前用户的学校匹配。
猜你喜欢
  • 2018-08-03
  • 2018-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 2021-07-25
相关资源
最近更新 更多