php - 如何在 Laravel 模型中使用带有 JOIN 和 GROUP BY 的自定义 SELECT?

标签 php laravel orm eloquent

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

具体来说,我想在我的应用程序中创建一个消息发送器。这是包含所有消息的“消息”表。现在我想创建名为“Dialog”的模型。请记住,这里没有表“对话框”,对话框是连接和分组的结果。

查询示例:

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 查询。但我想稍后使用 Eloquent 关系及其所有​​好处。例如:

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

我有一个想法,根据我的查询在数据库中创建一个 View ,并将该 View 用作模型中的假表。但在我看来这不是理想的解决方案。

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

最佳答案

您可以拥有一个没有 Eloquent 模型的数据库表,但反之则不然。也就是说,没有规定禁止每张 table 制作超过 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'))

关于php - 如何在 Laravel 模型中使用带有 JOIN 和 GROUP BY 的自定义 SELECT?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56185494/

相关文章:

php - 如果尚未附加 Laravel 关系附加

java - @OneToMany 表关系中的冗余行数据或实体

java - 在 hibernate 聚合函数中使用函数作为参数

php - PDO 停止执行重复 PK 上的事务

php - 将字符串中某个单词的某些字符设为粗体

laravel - Redis + Docker : PDOException: could not find driver

java - 持久化实体时出现 SQLServerException - 索引超出范围

javascript - 提交隐藏表单字段

php - 无法访问 PHP 基本身份验证变量

php - Docker - NGINX 容器转发到 PHP-FPM 容器