Laravel eloquent,当一行满足2个条件时,如何从查询中排除行?

Ibr*_*awa 6 php laravel eloquent

所以我想创建一个 Laravel 雄辩的查询。它有点复杂,并且有一堆子句,都运行良好,并且正在查询单个表。但是,我想添加一个特定条件,如下所述。

->where('date', '>', Carbon::now())
Run Code Online (Sandbox Code Playgroud)

此条件工作正常,但我希望此条件仅适用于特定行!假设我希望上述 where 子句适用的条件是:

->where('row_type', '=', 'someType')
Run Code Online (Sandbox Code Playgroud)

现在我不想过滤 row_type = 'someType' 的所有行,也不想过滤 date > Carbon::now() 的所有行。

我只想过滤日期 > Carbon::now() WHEN row_type = 'someType' 的行。

当然,'date' 和 'row_type' 都是我表中的列。

现在为了简化逻辑,我想要做的基本上是排除 (date < Carbon::now() AND row_type = 'someType') 都为真的行。

这是否可以在 eloquent 的单个查询中完成而不插入原始 sql?

我能够在原始 sql 中重现我的查询:

select id, date, row_type from my_table where case when row_type = 'someType' then date > '2019-03-01%' end;
Run Code Online (Sandbox Code Playgroud)

Džu*_*ris 0

现在为了简化逻辑,我想做的基本上是排除 (date < Carbon::now() AND row_type = 'someType') 都为 true 的行。

要么应用正式的布尔逻辑!(A and B) = !A or !B,要么只注意“排除两者都为真的情况”相当于“包括其中一个为假的情况”。因此,我们包括那些日期不是过去(即未来)或类型不是 someType 的行。

->where('row_type', '!=', 'someType')->orWhere('date', '>', Carbon::now())
Run Code Online (Sandbox Code Playgroud)

如果您还有其他条件并且包含这些条件orWhere会搞砸这些,那么您应该只使用嵌套:

// ...other wheres...
->where(function($query) {
    $query->where('date', '>', Carbon::now())->orWhere('row_type', '!=', 'someType');
})
->where( // ...other wheres...
Run Code Online (Sandbox Code Playgroud)

我将尝试通过 SQL 来证明这是可行的。

CREATE TABLE my_table(Id integer PRIMARY KEY, row_type text, date date);

/* First row is someType and past - it should be excluded */
INSERT INTO my_table VALUES(1,'someType', '2019-03-01');
INSERT INTO my_table VALUES(2,'someType', '2019-03-31');
INSERT INTO my_table VALUES(3,'otherType', '2019-03-01');
INSERT INTO my_table VALUES(4,'otherType', '2019-03-01');
COMMIT;
Run Code Online (Sandbox Code Playgroud)

op中的查询是这样的:

SELECT 'Cases from the OP' as '';
SELECT id, row_type, date 
FROM my_table
WHERE
  CASE
    WHEN row_type = 'someType'
      THEN date > '2019-03-22%'
  END;

/* returns */
2|someType|2019-03-31
Run Code Online (Sandbox Code Playgroud)

它甚至不做你所说的事情。它还排除 row_type 不是 someType 的每一行。这相当于row_type = 'someType' AND date > '2019-03-22'. 要使其排除您所说的应该排除的内容,您必须使其变得更加复杂:

SELECT id, row_type, date 
FROM my_table
WHERE
  CASE
    WHEN row_type = 'someType'
      THEN date > '2019-03-22'
    ELSE 1
  END;

/* returns */
2|someType|2019-03-31
3|otherType|2019-03-01
4|otherType|2019-03-01
Run Code Online (Sandbox Code Playgroud)

但这样写会更简单、更合适(实际有多种情况时才合适):

SELECT ' ' as '';
SELECT 'The same using OR' as '';
SELECT id, row_type, date
FROM my_table
WHERE
  (row_type != 'someType' OR date > '2019-03-22');

/* returns */
2|someType|2019-03-31
3|otherType|2019-03-01
4|otherType|2019-03-01
Run Code Online (Sandbox Code Playgroud)

我将条件括在括号中,因为您说您还希望添加其他语句。这就是我们where(function($q) {$q->...})要做的。