如何在Laravel模型中将自定义SELECT与JOIN和GROUP BY一起使用?

时间:2019-05-17 11:27:31

标签: php laravel orm eloquent

我想在Laravel模型中对JOIN和GROUP BY使用复杂的SELECT查询。

?我想在我的应用程序中创建一个消息程序。这是带有所有消息的表“消息”。现在,我要创建一个名为“对话框”的模型。请记住,这里没有表“对话框”,对话框是联接和分组的结果。

查询示例:

SELECT 
    cl.name                               AS client_name,
    COUNT(m.id)                           AS messages_count, 
    MAX(m.created_at)                     AS last_message,
    COUNT(m.id) > SUM(m.viewed_by_client) AS has_new_for_client,    
    COUNT(m.id) > SUM(m.viewed_by_user)   AS has_new_for_user

FROM messages AS m
INNER JOIN clients AS c ON m.client_id = c.id
GROUP BY c.id

当然,我可以使用原始SQL查询。但是我想在以后使用雄辩的关系及其所有​​好处。例如:

$dialog->client->full_name
$dialog->client->order->ordered_items

我有一个想法,可以从查询中在数据库中创建一个VIEW,并将该视图用作模型中的伪表。但是在我看来,这不是理想的解决方案。

那么,当我没有模型实体的真实表时,如何在Eloquent中使用JOIN和GROUP BY?还是针对我的任务有一些不同的解决方案?

2 个答案:

答案 0 :(得分:0)

创建对话框表,并将“ dialog_id”列放入消息表中。每个消息都有一个对话框和一个客户端。在每个模型中创建关系。因此,您可以根据需要访问模型上的属性。这样,此代码即可工作;

$dialog->client->full_name
$dialog->client->order->ordered_items

答案 1 :(得分:0)

您可以拥有没有Eloquent模型的数据库表,反之则没有。就是说,没有规则禁止每个表制作超过1个模型。不过,这不是真正的标准做法。

我尝试制作一个可以从另一个模型继承的模型,但是启动方法没有按预期工作,所以我删除了它。

我认为您可以在Client模型中使用访问者从该查询中获取所有信息。由于您的查询没有where子句,因此作用域并不是真正必要的,但也可以这样做。

选项1:访问器

# App\Client
class Client extends Model
{
    // Standard Eloquent relationship
    public function messages()
    {
        return $this->hasMany(App\Message::class);
    }
    // Accessor $client->client_name
    public function getClientNameAttribute()
    {
        return $this->name;
    }
    // Accessor $client->last_message
    public function getLastMessageAttribute()
    {
        // Load relationship only if it hasn't been loaded yet
        if(!$this->relationshipLoaded('messages'))
            $this->load('messages');
        // use max() method from collection to get the results
        return $this->messages->max('created_at');
    }

    // Accessor $client->has_new_for_client
    public function getHasNewForClientAttribute()
    {
        // Load relationship only if it hasn't been loaded yet
        if(!$this->relationshipLoaded('messages'))
            $this->load('messages');

        return $this->messages->count() > $this->messages->sum('viewed_by_client');
    }

    // Accessor $client->has_new_for_user
    public function getHasNewForUserAttribute()
    {
        // Load relationship only if it hasn't been loaded yet
        if(!$this->relationshipLoaded('messages'))
            $this->load('messages');

        return $this->messages->count() > $this->messages->sum('viewed_by_user');
    }
}

然后您可以动态访问所有属性

$dialog = Client::withCount('messages')->find($id);
$dialog->client_name;
$dialog->messages_count;
$dialog->has_new_for_client;
$dialog->has_new_for_user;
$dialog->last_message;

但是,如果您要将$dialog转换为数组或json格式,除非您附加,否则访问器将丢失。同样,您可以隐藏不想显示的属性。

这可以在模型的全局范围内完成

protected $appends = ['client_name', 'has_new_for_client', 'has_new_for_user', 'last_message'];
protected $hidden = ['name'];

或本地查询

$dialog->setHidden(['name']);
$dialog->setAppends(['client_name', 'has_new_for_client', 'has_new_for_user', 'last_message'];

选项2:查询范围

# App\Client
class Client extends Model
{
    public function scopeDialog($query)
    {
        $query->select('name as client_name')
              ->withCount('messages') // the default name will be messages_count
              ->selectRaw('max(m.created_at) as last_message')
              ->selectRaw('count(m.id) > sum(m.viewed_by_client) as has_new_for_client')
              ->selectRaw('count(m.id) > sum(m.viewed_by_user) as has_new_for_user')
              ->join('messages as m', 'm.client_id', 'clients.id')
              ->groupBy('clients.id');
    }
}

然后像调用任何范围Client :: dialog()-> ...

一样调用它

选项3:只需使用现有的任何方法即可,而不是编写更多的逻辑

$dialog = Client::with('messages')->find($id);
// client_name
$dialog->name
// messages_count
$dialog->messages->count()
// last_message
$dialog->messages->max('created_at')
// has_new_for_client
($dialog->messages->count('id') > $dialog->messages->count('viewed_by_client'))
// has_new_for_user
($dialog->messages->count('id') > $dialog->messages->count('viewed_by_user'))