在 Laravel Eloquent 中,如何在子查询中引用主查询
In Laravel Eloquent, how do I reference primary query in subquery
我有一个有很多订单的模型用户。订单有很多产品,主元 table order-product。如果可能的话,我不想预加载和遍历订单。
我需要 return 用户
- signed_date === 用户
为真
订单 - order_date 在用户
signed_date 之后
- order-product 显示产品尚未付款
我在 2 号上失败了。
在下面的代码中,whereHas 中的第一个查询是错误的。我不知道如何从 where has 中引用用户的签名日期。如果我在集合中遍历用户,我可以做类似 ($query) use $user 的事情,但是我如何在不预加载所有用户的情况下执行此操作?
return User::whereNotNull('signed_date')
->whereHas('orders', function ($query) {
$query->where('order_date', '<=', 'user.signed_date');
$query->whereHas('products', function ($q) {
$q->where('paid', false);
});
})
->get(['id','fname','lname', 'title', 'signed_date']);
如果可能,我想使用 eloquent。如果这不可能,我很乐意提供使用查询 builder/sql 解决此问题的提示。
Eloquent 查询构建器有一个名为 whereColumn('a', '<=', 'b')
的特殊函数,用于比较列而不是列与值。由于查询构建器构建实际查询的方式,使用此函数而不是正常的 where()
是必要的。您需要让查询构建器知道您将传递列名而不是值,以便正确转义和格式化查询字符串。
无论如何,您似乎还可以将以 table 名称为前缀的列名称传递给函数,从而允许您比较 tables:
中的列
$query->whereColumn('orders.order_date', '<=', 'users.signed_date')
之所以有效,是因为您在查询中使用了 whereHas()
。您的查询基本上被翻译成:
SELECT id, fname, lname, title, signed_date
FROM users
WHERE signed_date NOT NULL
AND EXISTS (
SELECT 1
FROM orders
WHERE orders.order_date <= users.signed_date
AND EXISTS (
SELECT 1
FROM products
WHERE paid = 0
)
)
实际上可能根本没有必要将 table 名称与 whereColumn()
中的列名称一起使用。但是,如果您在另一个 table 上添加同名的列,查询可能会中断 - 因此恕我直言,在自定义查询中使用 table 名称是一种很好的做法。
顺便说一句,这不能与 with('relationship')
一起工作的原因是这个函数会导致一个额外的查询,你显然不能跨查询比较列。想象一下:
Order::with('user')->take(5)->get();
它将被翻译成以下内容:
SELECT *
FROM orders
LIMIT 5
SELECT *
FROM users
WHERE id IN (?, ?, ?, ?, ?)
其中五个 ?
将是订单的 user_id
。如果第一个查询 returns 具有相同 user_id
的多行,从用户 table 获取的行数当然会减少。
注意:所有查询仅是示例。可能是查询构建器根据数据库类型构建不同的查询 and/or 以不同方式转义它们(即反引号中的列名)。
我有一个有很多订单的模型用户。订单有很多产品,主元 table order-product。如果可能的话,我不想预加载和遍历订单。
我需要 return 用户
- signed_date === 用户 为真 订单
- order_date 在用户 signed_date 之后
- order-product 显示产品尚未付款
我在 2 号上失败了。 在下面的代码中,whereHas 中的第一个查询是错误的。我不知道如何从 where has 中引用用户的签名日期。如果我在集合中遍历用户,我可以做类似 ($query) use $user 的事情,但是我如何在不预加载所有用户的情况下执行此操作?
return User::whereNotNull('signed_date')
->whereHas('orders', function ($query) {
$query->where('order_date', '<=', 'user.signed_date');
$query->whereHas('products', function ($q) {
$q->where('paid', false);
});
})
->get(['id','fname','lname', 'title', 'signed_date']);
如果可能,我想使用 eloquent。如果这不可能,我很乐意提供使用查询 builder/sql 解决此问题的提示。
Eloquent 查询构建器有一个名为 whereColumn('a', '<=', 'b')
的特殊函数,用于比较列而不是列与值。由于查询构建器构建实际查询的方式,使用此函数而不是正常的 where()
是必要的。您需要让查询构建器知道您将传递列名而不是值,以便正确转义和格式化查询字符串。
无论如何,您似乎还可以将以 table 名称为前缀的列名称传递给函数,从而允许您比较 tables:
中的列$query->whereColumn('orders.order_date', '<=', 'users.signed_date')
之所以有效,是因为您在查询中使用了 whereHas()
。您的查询基本上被翻译成:
SELECT id, fname, lname, title, signed_date
FROM users
WHERE signed_date NOT NULL
AND EXISTS (
SELECT 1
FROM orders
WHERE orders.order_date <= users.signed_date
AND EXISTS (
SELECT 1
FROM products
WHERE paid = 0
)
)
实际上可能根本没有必要将 table 名称与 whereColumn()
中的列名称一起使用。但是,如果您在另一个 table 上添加同名的列,查询可能会中断 - 因此恕我直言,在自定义查询中使用 table 名称是一种很好的做法。
顺便说一句,这不能与 with('relationship')
一起工作的原因是这个函数会导致一个额外的查询,你显然不能跨查询比较列。想象一下:
Order::with('user')->take(5)->get();
它将被翻译成以下内容:
SELECT *
FROM orders
LIMIT 5
SELECT *
FROM users
WHERE id IN (?, ?, ?, ?, ?)
其中五个 ?
将是订单的 user_id
。如果第一个查询 returns 具有相同 user_id
的多行,从用户 table 获取的行数当然会减少。
注意:所有查询仅是示例。可能是查询构建器根据数据库类型构建不同的查询 and/or 以不同方式转义它们(即反引号中的列名)。