audunru/eager-load-pivot-relations
Installation:
composer require audunru/eager-load-pivot-relations
No additional configuration is required—just use the trait in your models.
First Use Case:
For a BelongsToMany relationship where the pivot table has its own relations (e.g., Item ↔ Plan via plan_item with unit_id):
use audunru\EagerLoadPivotRelations\EagerLoadPivotTrait;
class Item extends Model
{
use EagerLoadPivotRelations;
public function plans()
{
return $this->belongsToMany(Plan::class)->withPivot('unit_id');
}
}
Eager-load the pivot relation (e.g., Unit for unit_id):
$items = Item::with('plans.unit')->get();
Define Pivot Relations:
Extend the pivot model or use withPivot() to specify relations:
// In Item model
public function plans()
{
return $this->belongsToMany(Plan::class)
->withPivot('unit_id')
->using(PlanItem::class); // Optional: Custom pivot model
}
Eager-Loading:
Use with() to load pivot relations alongside the main relationship:
// Load pivot relations for all items
$items = Item::with('plans.unit')->get();
// Load pivot relations conditionally
$items = Item::with(['plans.unit' => function ($query) {
$query->where('name', 'pc');
}])->get();
Accessing Data:
foreach ($items as $item) {
foreach ($item->plans as $plan) {
$unit = $plan->pivot->unit; // Access pivot relation
echo $unit->name; // e.g., "pc"
}
}
Custom Pivot Model:
If using a custom pivot model (e.g., PlanItem), define relations there:
class PlanItem extends Pivot
{
public function unit()
{
return $this->belongsTo(Unit::class);
}
}
Dynamic Eager-Loading: Use closures to conditionally load pivot relations:
$items = Item::when($loadUnits, function ($query) {
return $query->with('plans.unit');
})->get();
API Responses: Serialize pivot relations directly in JSON:
return Item::with('plans.unit')->get()->map(function ($item) {
return [
'name' => $item->name,
'plans' => $item->plans->map(function ($plan) {
return [
'plan_id' => $plan->id,
'unit' => $plan->pivot->unit->name,
];
}),
];
});
N+1 Queries Without with():
Forgetting to use with('plans.unit') will trigger N+1 queries when accessing pivot relations.
Fix: Always eager-load pivot relations explicitly.
Pivot Model Not Defined:
If using a custom pivot model (e.g., PlanItem), ensure it’s specified in the relationship:
return $this->belongsToMany(Plan::class)->using(PlanItem::class);
Fix: Add ->using(PivotModel::class) to the relationship.
Circular References:
Deeply nested pivot relations (e.g., unit.category) may cause serialization issues.
Fix: Use ->with() selectively or exclude problematic relations with ->without().
Laravel Version Mismatch: The package supports Laravel 8+. Using it on older versions may break. Fix: Check the releases for compatibility.
Query Logging: Enable query logging to verify eager-loading works:
DB::enableQueryLog();
$items = Item::with('plans.unit')->get();
dd(DB::getQueryLog());
Check Pivot Relations: Inspect the pivot model’s relations:
dd($item->plans->first()->pivot->unit); // Should return the Unit model
Fallback for Missing Relations: Handle cases where pivot relations might be null:
$unitName = $plan->pivot->unit?->name ?? 'N/A';
Customizing Pivot Loading:
Override the eagerLoadPivotRelations method in the trait for custom logic:
protected function eagerLoadPivotRelations(array $relations)
{
// Custom logic here
return parent::eagerLoadPivotRelations($relations);
}
Global Eager-Loading: Use model observers or service providers to auto-load pivot relations:
Item::observe(PlanItemObserver::class);
class PlanItemObserver {
public function retrieved(PlanItem $planItem) {
$planItem->load('unit');
}
}
Performance Optimization:
$items = Item::with(['plans.unit' => function ($query) {
$query->select('id', 'name'); // Limit columns
}])->get();
$unit = Cache::remember("unit_{$plan->pivot->unit_id}", now()->addHours(1), function () {
return Unit::find($plan->pivot->unit_id);
});
How can I help you explore Laravel packages today?