zing/laravel-eloquent-relationships
Installation:
composer require zing/laravel-eloquent-relationships
Publish the config (if needed):
php artisan vendor:publish --provider="Zing\LaravelEloquentRelationships\ServiceProvider"
First Use Case: Define a custom relationship in your Eloquent model:
use Zing\LaravelEloquentRelationships\Relationships\HasManyThrough;
class User extends Model
{
public function ordersThroughAdmin()
{
return $this->hasManyThrough(
Order::class,
Admin::class,
'user_id', // Local key on User
'admin_id', // Foreign key on Admin
'id', // Local key on Admin
'user_id' // Foreign key on Order
);
}
}
Key Files:
config/zing-eloquent-relationships.php (if published)app/Models/ (for custom relationship definitions)vendor/zing/laravel-eloquent-relationships/src/ (for advanced usage)Use HasManyThrough for nested relationships:
// User → Admin → Order
$user->ordersThroughAdmin()->get();
Extend polymorphic behavior with custom logic:
use Zing\LaravelEloquentRelationships\Relationships\MorphToMany;
class Post extends Model
{
public function tags()
{
return $this->morphToMany(
Tag::class,
'taggable',
'post_tag',
'tag_id',
'taggable_id',
'taggable_type'
);
}
}
Dynamically toggle relationships based on logic:
public function activeOrders()
{
return $this->hasMany(Order::class)
->when($this->isPremium(), function ($query) {
return $query->where('status', 'active');
});
}
Attach query constraints to relationships:
use Zing\LaravelEloquentRelationships\Traits\ScopesRelationships;
class User extends Model
{
use ScopesRelationships;
public function scopeActive($query)
{
return $query->where('active', true);
}
public function orders()
{
return $this->hasMany(Order::class)->active();
}
}
Cache relationship results for performance:
public function orders()
{
return $this->hasMany(Order::class)->remember();
}
Service Providers:
Register custom relationship macros in AppServiceProvider:
use Zing\LaravelEloquentRelationships\Facades\Relationship;
public function boot()
{
Relationship::macro('customRelation', function () {
return $this->hasMany(CustomModel::class);
});
}
API Responses: Use relationships in API resources:
public function toArray($request)
{
return [
'orders' => $this->ordersThroughAdmin()->take(5)->get(),
];
}
Testing: Mock relationships in PHPUnit:
$user = User::factory()->create();
$user->shouldReceive('ordersThroughAdmin')
->once()
->andReturn(collect([Order::factory()->make()]));
Database Observers: Trigger logic on relationship changes:
Order::observe(OrderObserver::class);
class OrderObserver
{
public function saved(Order $order)
{
$order->user->touch(); // Update user's last_order_at
}
}
N+1 Queries: Always eager-load relationships to avoid N+1 issues:
$users = User::with('ordersThroughAdmin')->get();
Circular References: Avoid infinite loops in serializable models:
public function toArray()
{
return [
'id' => $this->id,
'orders' => $this->ordersThroughAdmin->pluck('id'), // Avoid loading full objects
];
}
Foreign Key Mismatches:
Double-check foreign key names in HasManyThrough:
// Wrong: Assumes default keys
$this->hasManyThrough(Order::class, Admin::class);
// Correct: Explicit keys
$this->hasManyThrough(Order::class, Admin::class, 'user_id', 'admin_id', 'id', 'user_id');
Polymorphic Ambiguity:
Ensure taggable_type is consistent across models in MorphToMany.
Cache Invalidation: Clear cached relationships after data changes:
$user->ordersThroughAdmin()->fresh();
Query Logging: Enable Eloquent query logging:
DB::enableQueryLog();
$user->ordersThroughAdmin()->get();
dd(DB::getQueryLog());
Relationship Introspection: Debug relationship definitions:
dd($user->ordersThroughAdmin->getQuery()->toSql());
Macro Conflicts: Check for naming collisions in custom macros:
Relationship::hasMacro('customRelation') // Verify before registering
Performance Optimization:
Use select() to limit loaded columns:
$user->ordersThroughAdmin()->select('id', 'amount');
Dynamic Relationships: Build relationships dynamically:
public function getRelationValue($key)
{
if ($key === 'dynamicOrders') {
return $this->hasMany(Order::class)->where('status', 'pending');
}
return parent::getRelationValue($key);
}
Configuration Overrides:
Override default behavior in config/zing-eloquent-relationships.php:
'default_relationships' => [
'orders' => 'ordersThroughAdmin',
],
Laravel Scout Integration: Index relationships for search:
use Zing\LaravelEloquentRelationships\Scout\Searchable;
class Order extends Model
{
use Searchable;
public function searchableAttributes()
{
return ['user_id', 'admin_id'];
}
}
API Rate Limiting: Limit relationship queries in API routes:
Route::middleware(['throttle:60,1'])->group(function () {
Route::get('/users/{user}/orders', [UserController::class, 'orders']);
});
Soft Deletes: Handle soft-deleted relationships:
$user->ordersThroughAdmin()->withTrashed()->get();
How can I help you explore Laravel packages today?