reedware/laravel-composite-relations
composer require reedware/laravel-composite-relations
Model class:
use Reedware\LaravelCompositeRelations\HasCompositeRelations;
abstract class Model extends Eloquent {
use HasCompositeRelations;
}
belongsTo with compositeBelongsTo:
public function userRole() {
return $this->compositeBelongsTo(UserRole::class, ['user_id', 'role_id'], ['user_id', 'role_id']);
}
$userRole = UserRole::find(['user_id' => 1, 'role_id' => 2]);
$userRole->user()->first(); // Test eager loading
release/v5.0.0.md for Laravel 11/12-specific changes (e.g., generics support).compositeBelongsTo, compositeHasOne, and compositeHasMany methods.Legacy System Integration: Convert a raw SQL query like:
SELECT * FROM user_roles ur
JOIN users u ON ur.user_id = u.id AND ur.role_id = u.default_role_id
WHERE ur.user_id = 1;
into an Eloquent relation:
public function defaultUserRole() {
return $this->compositeBelongsTo(User::class, ['user_id', 'role_id'], ['id', 'default_role_id']);
}
// BelongsTo
$this->compositeBelongsTo(RelatedModel::class, ['local_key1', 'local_key2'], ['foreign_key1', 'foreign_key2']);
// HasOne/HasMany
$this->compositeHasOne(RelatedModel::class, ['foreign_key1', 'foreign_key2'], ['local_key1', 'local_key2']);
$primaryKeys on the related model to omit keys:
class UserRole extends Model {
protected $primaryKeys = ['user_id', 'role_id'];
}
// Usage:
$this->compositeBelongsTo(UserRole::class);
$models = Model::with('compositeRelation')->get();
$models = Model::whereHas('compositeRelation', function($query) {
$query->where('role_id', '>', 1);
})->get();
reedware/laravel-relation-joins:
$task->joinRelation('importSummary', function($join) {
$join->where('task_import_summaries.name', 'like', '%test%');
});
'and' for strict matching (default: 'or'):
$this->compositeBelongsTo(Related::class, ['key1', 'key2'], ['fk1', 'fk2'], null, 'and');
-- 'or' (default)
WHERE (fk1 = ? OR fk2 = ?) OR (fk1 = ? OR fk2 = ?)
-- 'and'
WHERE (fk1 = ? AND fk2 = ?) OR (fk1 = ? AND fk2 = ?)
public function test_composite_belongs_to() {
$role = UserRole::find(['user_id' => 1, 'role_id' => 2]);
$this->assertInstanceOf(User::class, $role->user()->first());
}
toSql() and getBindings() to verify generated queries:
$query = $model->compositeRelation()->toSql();
$bindings = $model->compositeRelation()->getBindings();
ALTER TABLE related_table ADD INDEX composite_index (foreign_key1, foreign_key2);
Model::with(['compositeRelation1', 'compositeRelation2'])->get();
id). If the related model also has composite keys, queries may fail.getForeignKeys() method in the related model:
class RelatedModel extends Model {
public function getForeignKeys() {
return ['fk1', 'fk2']; // Explicitly define foreign keys
}
}
'and' with mismatched key counts can produce incorrect queries. Example:
// ❌ Wrong: 3 local keys vs. 2 foreign keys
$this->compositeBelongsTo(Related::class, ['key1', 'key2', 'key3'], ['fk1', 'fk2'], null, 'and');
'or' for flexibility.// ❌ May not apply constraints correctly
Model::with(['compositeRelation' => function($query) {
$query->where('role_id', '>', 1);
}])->get();
whereHas for constraints:
Model::whereHas('compositeRelation', function($query) {
$query->where('role_id', '>', 1);
})->with('compositeRelation')->get();
morphTo with composite keys).DB::enableQueryLog();
$model->compositeRelation()->get();
dd(DB::getQueryLog());
$query = $model->compositeRelation()->toSql();
$bindings = $model->compositeRelation()->getBindings();
dd($query, $bindings);
| Error | Cause | Solution |
|---|---|---|
Column not found |
Mismatched key names in compositeBelongsTo |
Verify table columns and key arrays. |
Call to undefined method |
Missing HasCompositeRelations trait |
Ensure the trait is added to the base model. |
SQLSTATE[23000] |
Duplicate composite keys | Add UNIQUE constraints or handle duplicates in application logic. |
Binding mismatch |
Incorrect glue operator usage | Use 'or' for flexibility or 'and' for strict matching. |
// config/composite-relations.php
'glue' => 'and', // Default glue for all relations
belongsToMany support:
How can I help you explore Laravel packages today?