Laravel Eloquent - 对多个关系列求和

Cai*_*aki 5 mysql laravel eloquent

我不是Eloquent大师,我做了很多研究,无法重现我的预期。

我有以下型号:

<?php

use Illuminate\Database\Eloquent\Model;

class Invoice extends Model
{
    protected $fillable = [
        'uuid',
        'number_id'
    ];

    protected $dates = [
        'started_at',
        'ended_at'
    ];

    public $timestamps = false;


    public function contacts()
    {
        return $this->hasMany(Contact::class);
    }
}
Run Code Online (Sandbox Code Playgroud)
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
    protected $fillable = [
        'incoming_messages',
        'outgoing_messages',
        'outgoing_template_messages',
    ];

    public $timestamps = false;


    public function invoice()
    {
        return $this->belongsTo(Invoice::class);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我搜索特定的时Invoice::class

$invoice = Invoice::number($number)
    ->latest('started_at')
    ->with(['contacts'])
    ->firstOrFail();
Run Code Online (Sandbox Code Playgroud)

我需要它以这种方式返回:

{
    "id": 1,
    "number_id": "444a13fd-9789-426e-bdfb-c3e2e83c4422",
    "incoming_messages": 10, #(sum of invoice.contacts.incoming_messages)
    "outgoing_messages": 10, #(sum of invoice.contacts.outgoing_messages)
    "outgoing_template_messages": 10, #(sum of invoice.contacts.outgoing_template_messages)
    "started_at": "2020-07-01T00:00:00.000000Z",
    "ended_at": "2020-07-31T23:59:59.000000Z"
}
Run Code Online (Sandbox Code Playgroud)

我目前正在这样做InvoiceResource::class

{
    "id": 1,
    "number_id": "444a13fd-9789-426e-bdfb-c3e2e83c4422",
    "incoming_messages": 10, #(sum of invoice.contacts.incoming_messages)
    "outgoing_messages": 10, #(sum of invoice.contacts.outgoing_messages)
    "outgoing_template_messages": 10, #(sum of invoice.contacts.outgoing_template_messages)
    "started_at": "2020-07-01T00:00:00.000000Z",
    "ended_at": "2020-07-31T23:59:59.000000Z"
}
Run Code Online (Sandbox Code Playgroud)

但是数据量非常大,我想删除EagerLoading并在单个查询中完成所有操作以减少对数据库的影响。

关于如何使用 just 解决这个问题的任何想法Eloquent


更新

一位朋友帮助我完成了我想转换为的查询Eloquent

$incoming_messages = $this->contacts->sum('incoming_messages');
$outgoing_messages = $this->contacts->sum('outgoing_messages');
$outgoing_template_messages = $this->contacts->sum('outgoing_template_messages');
Run Code Online (Sandbox Code Playgroud)

Cai*_*aki 3

我设法仅使用 eloquent 进行查询。我的数据库性能如预期发生了巨大变化。

这是仅使用 eloquent 进行的查询:

$invoice = Invoice::number($number)
    ->selectRaw('invoices.uuid as uuid,
    invoices.number_id as number_id,
    count(*) as contacts,
    sum(incoming_messages) as incoming_messages,
    sum(outgoing_messages) as outgoing_messages,
    sum(outgoing_template_messages) as outgoing_template_messages,
    invoices.started_at,
    invoices.ended_at')
    ->join('contacts', 'contacts.invoice_id', '=', 'invoices.id')
    ->groupBy('invoices.id')
    ->latest('started_at')
    ->firstOrFail();
Run Code Online (Sandbox Code Playgroud)

更新

我遇到了一个问题,我的查询没有返回没有任何联系人的发票,我不得不做一个小改变。

join()方法生成一个inner joininner join不包括table A中没有元素的行table B

为了克服这种情况,left join必须使用a。但是,如果left join中没有值table B,则它会将我的总和返回为null

为了解决这个问题,我简单地使用了 MySql 函数ifnull()

这是最终的查询:

$invoices = Invoice::number($number)
    ->selectRaw('invoices.uuid as uuid,
    invoices.number_id as number_id,
    ifnull(count(contacts.id), 0) as contacts,
    ifnull(sum(incoming_messages), 0) as incoming_messages,
    ifnull(sum(outgoing_messages), 0) as outgoing_messages,
    ifnull(sum(outgoing_template_messages), 0) as outgoing_template_messages,
    invoices.started_at,
    invoices.ended_at')
    ->leftJoin('contacts', 'contacts.invoice_id', '=', 'invoices.id')
    ->groupBy('invoices.id')
    ->firstOrFail();
Run Code Online (Sandbox Code Playgroud)