我正在使用Laravel 5,在让->wherePivot()
处理多对多关系时遇到问题。当我dd()
SQL时,看起来Eloquent正在数据透视表中查找具有"pose_state"的记录`pose_id `为null `。
我希望这是一个简单的错误,而不是一个错误。欢迎提出任何想法。
数据库结构
姿势
id
name
type
状态
id
name
machine_name
pose_state
pose_id
state_id
status
型号
姿势
<?php namespace App;
use DB;
use App'State;
use Illuminate'Database'Eloquent'Model;
class Pose extends Model {
public function states()
{
return $this->belongsToMany('App'State')
->withPivot('status_id')
->withTimestamps();
}
public function scopeWithPendingReviews()
{
return $this->states()
->wherePivot('status_id',10);
}
}
状态
<?php namespace App;
use Illuminate'Database'Eloquent'Model;
class State extends Model {
public function poses()
{
return $this->belongsToMany('Pose')
->withPivot('status_id')
->withTimestamps();
}
}
姿态控制器功能
public function listPosesForReview(){
$poses = Pose::withPendingReviews()->get();
dd($poses->toArray() );
}
SQL
select
`states`.*, `pose_state`.`pose_id` as `pivot_pose_id`,
`pose_state`.`state_id` as `pivot_state_id`,
`pose_state`.`status_id` as `pivot_status_id`,
`pose_state`.`created_at` as `pivot_created_at`,
`pose_state`.`updated_at` as `pivot_updated_at`
from
`states` inner join `pose_state` on `states`.`id` = `pose_state`.`state_id`
where
`pose_state`.`pose_id` is null and `pose_state`.`status_id` = ?
编辑
当我更新代码以删除作用域时,它起作用了。感谢@Deefour让我走上了正确的道路!也许scope还有其他我所缺少的东西。
public function pendingReviews()
{
return $this->states()
->wherePivot('status_id','=', 10);
}
又一次编辑
我终于做到了。上面的解决方案是给我重复的条目。不知道为什么这有效,但它确实有效,所以我会坚持下去。
public function scopeWithStatusCode($query, $tag)
{
$query->with(['states' => function($q) use ($tag)
{
$q->wherePivot('status_id','=', $tag);
}])
->whereHas('states',function($q) use ($tag)
{
$q->where('status_id', $tag);
});
}
我认为scopeWithPendingReviews()
的实现是对作用域预期用途的滥用。
作用域应该被认为是一组可重用的条件,可以附加到现有查询中,即使该查询只是
SomeModel::newQuery()
其想法是,scope方法中的条件将对预先存在的查询进行进一步细化(读作:"scoped"),而不是生成新查询,也绝对不是基于关联模型生成新查询。
默认情况下,传递给作用域方法的第一个也是唯一一个参数是查询生成器实例本身。
您在Pose
模型上的作用域实现实际上是对states
表的查询
$this->states()
这就是为什么您的SQL会如此显示。这也是一个明显的迹象,表明你滥用范围。范围可能看起来像这个
public function scopeWithPendingReviews($query) {
$query->join('pose_state', 'poses.id', '=', 'pose_state.pose.id')
->where('status_id', 10);
}
与返回基于State
模型的查询的新pendingReviews()
方法不同,此作用域将细化基于Pose
模型的查询。
现在您可以按照最初的意图使用您的范围。
$poses = Pose::withPendingReviews();
它可以被翻译成更详细的
$poses = Pose::newQuery()->withPendingReviews();
还要注意,上面的作用域没有返回值。它接受现有的查询生成器对象并添加到其中
这个问题的另一个答案充满了错误信息。
- 不能按原样使用
wherePivot()
声明 - 你使用
withTimestamps()
与你的问题完全无关 - 你不必做任何";定制工作";让时间戳发挥作用。只需要像您所做的那样添加
withTimestamps()
调用。只需确保联接表中有一个created_at
和updated_at
列
我认为您的作用域实现很好,我看到的问题只是一个拼写错误。您的模式显示该字段被称为status
,但您的where条件指的是status_id
尝试:
->wherePivot('status', 10);
此外,withTimestamps()
方法也引起了问题。你的模式中没有数据透视的时间戳(正如我所看到的),所以你不应该把这些放在你的关系定义中,因为它试图获取与创建/更新关系时相关的时间戳。如果您将透视表模式设置为具有时间戳字段,则可以这样做,但我认为您必须进行一些自定义工作才能正确保存时间戳。
这对我有效(Laravel 5.3):
$task = App'Models'PricingTask::find(1);
$task->products()->wherePivot('taggable_type', 'product')->get();
如果您在wherePivot
中使用的列尚未添加到withPivot
中,也可能出现此问题(不返回结果)。