我想在 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/