首页 > 解决方案 > Laravel Eloquent Accessor 奇怪的问题

问题描述

Laravel Version: 5.6.39
PHP Version: 7.1.19
Database Driver & Version: mysql 5.6.43

描述:

当我在模型访问器中链接 where 和 orWhere 以计算相关模型时,我得到错误的结果,这是我的查询。计数返回奇怪的结果,没有通过调用事件 id 过滤,

class Event extends Model
{
    protected $table = 'events';
    public function registrations()
    {
        return $this->hasMany('App\Components\Event\Models\Registration','event_id','id');
    }

    public function getSeatsBookedAttribute()
    {
        return $this->registrations()          
            ->where('reg_status','=','Confirmed')
            ->orWhere('reg_status','=','Reserved')
            ->count();
    }
}

重现步骤:

以下查询返回我预期的结果,但是据我所知,如果我没有错,第一个查询应该返回相同的结果,所以我认为这是一个潜在的错误。

class Event extends Model
{
    public function getSeatsBookedAttribute()
    {
        return $this->registrations()          
             ->whereIn('reg_status', ['Confirmed', 'Reserved'])
            ->count();
    }

}


class Event extends Model
{
    public function getSeatsBookedAttribute()
    {
        return $this->registrations()          
          ->where(function($query){
                $query->where('reg_status','Confirmed')
                    ->orWhere('reg_status','Reserved');
           })
            ->count();
    }

}

这是查询转储,

这是我没有明确分组时的查询。

"select count(*) as aggregate from events_registration where (events_registration.event_id = ? and events_registration.event_id is not null and reg_status = ? or reg_status = ?) and events_registration.deleted_at is null "

这是我明确分组时的查询,

select count(*) as aggregate from events_registration where events_registration.event_id = ? and events_registration.event_id is not null and (reg_status = ? or reg_status = ?) and events_registration.deleted_at is null 

标签: laraveleloquent

解决方案


发生这种情况的原因是因为您正在链接where()orWhere(). 您在幕后看不到的是where event_id = :event_id对您的查询的应用。您最终会得到一个如下所示的查询:

select * from registrations where event_id = :event_id and reg_status = 'Confirmed' or reg_status = 'Reserved'

在普通 SQL 中,您希望将最后 2 个条件放在括号中。对于 Eloquent,您需要执行以下操作:

return $this->registrations()->where(function ($query) {
   $query->where('reg_status', 'Confirmed')
      ->orWhere('reg_status', 'Reserved');
});

您可以将toSql()方法链接到这些链上以查看差异。请注意,在这种情况下,我认为这whereIn()是语义上正确的做法。

不过,Eloquent 可以为您处理这个问题;向下滚动到 Eloquent 关系文档的查询关系部分中的“计数相关模型” :

$posts = App\Event::withCount([
    'registrations as seats_booked_count' => function ($query) {
            $query->where('reg_status','Confirmed')
                ->orWhere('reg_status','Reserved');
    }
])->get();

推荐阅读