reedware/laravel-composite-relations
Installation:
composer require reedware/laravel-composite-relations
No publisher required—just install and use.
First Use Case:
Define a composite belongsTo relation in a model (e.g., OrderItem):
use Reedware\CompositeRelations\HasCompositeRelations;
class OrderItem extends Model
{
use HasCompositeRelations;
public function order()
{
return $this->belongsTo(
Order::class,
['order_id', 'user_id'], // Composite keys
['id', 'user_id'] // Composite keys on parent
);
}
}
Where to Look First:
Defining Relations:
return $this->belongsTo(
RelatedModel::class,
['fk1', 'fk2'], // Child keys
['pk1', 'pk2'] // Parent keys
);
return $this->hasOne(
RelatedModel::class,
['parent_fk1', 'parent_fk2'],
['fk1', 'fk2']
);
return $this->belongsToPolymorphic(
['fk1', 'fk2', 'fk3'],
['parent_pk1', 'parent_pk2']
);
Querying:
$orderItem->order; // Accessor
$orderItem->load('order'); // Eager loading
OrderItem::whereComposite('order_id', 'user_id', '=', [1, 2])->get();
Migrations:
foreignId():
$table->foreignId('fk1')->constrained('orders', 'id');
$table->foreignId('fk2')->constrained('orders', 'user_id');
Integration with Existing Code:
// Before (manual join)
DB::table('order_items')
->join('orders', function ($join) {
$join->on('order_items.order_id', '=', 'orders.id')
->on('order_items.user_id', '=', 'orders.user_id');
});
// After (composite relation)
OrderItem::with('order')->get();
API Responses:
with() to include nested composite relations in JSON:
return OrderItem::with(['order', 'order.user'])->get();
Key Order Sensitivity:
['fk1', 'fk2'] with ['fk2', 'fk1'] will fail silently (no match found).Eager Loading Quirks:
// Avoid:
foreach ($orderItems as $item) { $item->order; } // N+1
// Prefer:
OrderItem::with('order')->get();
Polymorphic Relations:
type column must still exist for polymorphic relations, even with composite keys.type column isn’t needed.Mass Assignment:
save() or associate() manually:
$orderItem->order()->associate($order);
$orderItem->save();
Soft Deletes:
withTrashed() if needed:
$this->belongsTo(Order::class, ['fk1', 'fk2'], ['pk1', 'pk2'])
->withTrashed();
Query Logs:
config/database.php:
'log_queries' => true,
storage/logs/laravel.log for malformed composite joins.Common Errors:
RelationNotFound: Verify composite key columns exist in both tables.SQLSTATE[23000]: Foreign key constraint violations (check key orders).Testing:
CompositeRelationTestCase from the package’s tests as a template for your own tests.Custom Constraints:
CompositeRelation::getQuery() to add custom where clauses:
$this->belongsTo(Order::class, ['fk1', 'fk2'], ['pk1', 'pk2'])
->constrain(function ($query) {
$query->where('orders.status', 'active');
});
Composite Primary Keys:
$this->belongsTo(
Order::class,
['order_fk1', 'order_fk2'],
['pk1', 'pk2'] // Parent's composite PK
);
Dynamic Composite Keys:
$this->belongsTo(Order::class, function () {
return ['order_' . tenantId(), 'user_' . auth()->id()];
}, ['id', 'user_id']);
Performance:
$table->index(['fk1', 'fk2']);
How can I help you explore Laravel packages today?