igunultas/eager-load-pivot-relations

为Laravel Eloquent的BelongsToMany关系预加载枢纽关系。

v0.2.2 2019-07-26 23:26 UTC

This package is auto-updated.

Last update: 2024-09-27 11:12:56 UTC


README

为Laravel Eloquent的BelongsToMany关系预加载枢纽关系。
Medium故事: https://medium.com/@ajcastro29/laravel-eloquent-eager-load-pivot-relations-dba579f3fd3a

安装

composer require ajcastro/eager-load-pivot-relations

用法和示例

在某些情况下,枢纽模型有需要预加载的关系。例如,在一个采购系统中,我们有以下

表格

items
 - id
 - name

units
 - id
 - name (pc, box, etc...)

plans (annual procurement plan)
 - id

plan_item (pivot for plans and items)
 - id
 - plan_id
 - item_id
 - unit_id

模型

class Unit extends \Eloquent {
}

use AjCastro\EagerLoadPivotRelations\EagerLoadPivotTrait;
class Item extends \Eloquent
{
    // Use the trait here to override eloquent builder.
    // It is used in this model because it is the relation model defined in
    // Plan::items() relation.
    use EagerLoadPivotTrait;

    public function plans()
    {
        return $this->belongsToMany('Plan', 'plan_item');
    }
}

class Plan extends \Eloquent
{
    public function items()
    {
        return $this->belongsToMany('Item', 'plan_item')
            ->using('PlanItem')
            // make sure to include the necessary foreign key in this case the `unit_id`
            ->withPivot('unit_id', 'qty', 'price');
    }
}


// Pivot model
class PlanItem extends \Illuminate\Database\Eloquent\Relations\Pivot
{
    protected $table = 'plan_item';

    public function unit()
    {
        return $this->belongsTo('Unit');
    }
}

从上面的代码中,plansitems具有多对多关系。计划中的每个项目都有一个选定的unit,即计量单位。枢纽模型也可能在其他场景下有其他多对多关系。

预加载枢纽关系

在预加载枢纽模型时使用关键字pivot。所以从上面的例子中,枢纽模型PlanItem可以通过这样做来预加载unit关系

return Plan::with('items.pivot.unit')->get();

结果的数据结构将是

image

您也可以访问其他关系,例如

return Plan::with([
  'items.pivot.unit',
  'items.pivot.unit.someRelation',
  'items.pivot.anotherRelation',
  // It is also possible to eager load nested pivot models
  'items.pivot.unit.someBelongsToManyRelation.pivot.anotherRelationFromAnotherPivot',
])->get();

自定义枢纽访问器

您可以自定义“枢纽访问器”,因此我们不需要使用关键字pivot,我们可以将其声明为planItem。只需在BelongsToMany关系的定义中链式调用as()方法。

class Plan extends \Eloquent
{
    public function items()
    {
        return $this->belongsToMany('Item', 'plan_item')
            ->withPivot('unit_id', 'qty', 'price')
            ->using('PlanItem')
            ->as('planItem');
    }
}

确保我们也在我们的主要模型(即Plan模型)上使用这个特性,因为该包需要访问belongsToMany关系(items关系)以识别使用的枢纽访问器。

use AjCastro\EagerLoadPivotRelations\EagerLoadPivotTrait;

class Plan extends \Eloquent
{
    use EagerLoadPivotTrait;
}

因此,我们可以通过定义的枢纽访问器planItem来预加载它,而不是使用pivot

return Plan::with('items.planItem.unit')->get();
$plan = Plan::with('items.planItem.unit');

foreach ($plan->items as $item) {
    $unit = $item->planItem->unit;
    echo $unit->name;
}

其他示例和用例

https://github.com/ajcastro/eager-load-pivot-relations-examples