-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 67419c5
Showing
4 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"name": "ajcastro/eager-load-pivot-relations", | ||
"description": "Eager load pivot relations for Laravel Eloquent's BelongsToMany relation.", | ||
"type": "library", | ||
"license": "MIT", | ||
"authors": [ | ||
{ | ||
"name": "Arjon Jason Castro", | ||
"email": "ajcastro29@gmail.com" | ||
} | ||
], | ||
"require": { | ||
"php": ">=5.4.0" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"AjCastro\\EagerLoadPivotRelations\\": "src" | ||
} | ||
}, | ||
"extra": { | ||
"laravel": { | ||
"providers": [ | ||
], | ||
"aliases": { | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# Laravel Eloquent: Eager Load Pivot Relations | ||
|
||
Eager load pivot relations for Laravel Eloquent's BelongsToMany relation. | ||
|
||
## Installation | ||
|
||
``` | ||
composer require ajcastro/eager-load-pivot-relations | ||
``` | ||
|
||
## Usage and Example | ||
|
||
There are use-cases where in a pivot model has relations to be eager loaded. | ||
Example, in a procurement system, we have the following: | ||
|
||
**Tables** | ||
|
||
``` | ||
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 | ||
``` | ||
|
||
**Models** | ||
|
||
```php | ||
|
||
class Unit extends \Eloquent { | ||
protected $fillable = []; | ||
} | ||
|
||
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; | ||
|
||
protected $fillable = []; | ||
|
||
public function plans() | ||
{ | ||
return $this->belongsToMany('Plan', 'plan_item'); | ||
} | ||
} | ||
|
||
class Plan extends \Eloquent | ||
{ | ||
protected $fillable = []; | ||
|
||
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'); | ||
} | ||
} | ||
``` | ||
|
||
From the code above, `plans` and `items` has `Many-to-Many` relationship. Each item in a plan has a selected `unit`, unit of measurement. | ||
It also possible for other scenario that the pivot model will have other many relations. | ||
|
||
## Eager Loading Pivot Relations | ||
|
||
Use keyword `pivot` in eager loading pivot models. So from the example above, the pivot model `PlanItem` can eager load the `unit` relation by doing this: | ||
|
||
``` | ||
return Plan::with('items.pivot.unit')->get(); | ||
``` | ||
|
||
The resulting data structure will be: | ||
|
||
![image](https://cloud.githubusercontent.com/assets/4918318/17958278/0d3c962a-6acb-11e6-8415-c48d01457cd6.png) | ||
|
||
You may also access other relations for example: | ||
|
||
``` | ||
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(); | ||
``` | ||
|
||
## TODO/Need Help: | ||
|
||
I would like to support customising the `pivot` keyword. | ||
If you chain the `as()` method to define the __"pivot accessor"__ of the `BelongsToMany` relation, | ||
it should use the defined pivot accessor. | ||
|
||
```php | ||
class Plan extends \Eloquent | ||
{ | ||
protected $fillable = []; | ||
|
||
public function items() | ||
{ | ||
return $this->belongsToMany('Item', 'plan_item') | ||
->withPivot('unit_id', 'qty', 'price') | ||
->using('PlanItem') | ||
->as('planItem'); | ||
} | ||
} | ||
|
||
``` | ||
So instead of using `pivot`, we can eager load it by defined pivot accessor `planItem`. | ||
|
||
``` | ||
return Plan::with('items.planItem.unit')->get(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace AjCastro\EagerLoadPivotRelations; | ||
|
||
use Closure; | ||
|
||
class EagerLoadPivotBuilder extends \Illuminate\Database\Eloquent\Builder | ||
{ | ||
/** | ||
* Override. | ||
* Eagerly load the relationship on a set of models. | ||
* | ||
* @param array $models | ||
* @param string $name | ||
* @param \Closure $constraints | ||
* @return array | ||
*/ | ||
protected function eagerLoadRelation(array $models, $name, Closure $constraints) | ||
{ | ||
if ($name === 'pivot') { | ||
$this->eagerLoadPivotRelations($models); | ||
return $models; | ||
} | ||
|
||
return parent::eagerLoadRelation($models, $name, $constraints); | ||
} | ||
|
||
/** | ||
* Eager load pivot relations. | ||
* | ||
* @param array $models | ||
* @return void | ||
*/ | ||
protected function eagerLoadPivotRelations($models) | ||
{ | ||
$pivots = array_pluck($models, 'pivot'); | ||
$pivots = head($pivots)->newCollection($pivots); | ||
$pivots->load($this->getPivotEagerLoadRelations()); | ||
} | ||
|
||
/** | ||
* Get the pivot relations to be eager loaded. | ||
* | ||
* @return array | ||
*/ | ||
protected function getPivotEagerLoadRelations() | ||
{ | ||
$relations = array_filter(array_keys($this->eagerLoad), function ($relation) { | ||
return $relation != 'pivot' && str_contains($relation, 'pivot'); | ||
}); | ||
return array_map(function ($relation) { | ||
return substr($relation, strlen('pivot.')); | ||
}, $relations); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace AjCastro\EagerLoadPivotRelations; | ||
|
||
trait EagerLoadPivotTrait | ||
{ | ||
/** | ||
* Create a new Eloquent query builder for the model. | ||
* | ||
* @param \Illuminate\Database\Query\Builder $query | ||
* @return \Illuminate\Database\Eloquent\Builder|static | ||
*/ | ||
public function newEloquentBuilder($query) | ||
{ | ||
return new EagerLoadPivotBuilder($query); | ||
} | ||
} |