staudenmeir / laravel-merged-relations
Merged Laravel Eloquent relationships
Fund package maintenance!
paypal.me/JonasStaudenmeir
Installs: 103 342
Dependents: 0
Suggesters: 0
Security: 0
Stars: 178
Watchers: 4
Forks: 11
Open Issues: 2
Requires
- php: ^8.2
- illuminate/database: ^11.0
- staudenmeir/laravel-migration-views: ^1.9.1
Requires (Dev)
- barryvdh/laravel-ide-helper: ^3.0
- orchestra/testbench: ^9.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.0
- staudenmeir/eloquent-has-many-deep: ^1.20
README
This Laravel Eloquent extension allows merging multiple relationships using SQL views.
The relationships can target the same or different related models.
Supports Laravel 5.5+.
Installation
composer require staudenmeir/laravel-merged-relations:"^1.0"
Use this command if you are in PowerShell on Windows (e.g. in VS Code):
composer require staudenmeir/laravel-merged-relations:"^^^^1.0"
Versions
Usage
- Use Cases
- Step 1: Creating Views
- Step 2: Defining Relationships
- Pivot Table Data
- Limitations
- Testing
Use Cases
Use the package to merge multiple polymorphic relationships:
class Tag extends Model { public function allTaggables() { // TODO } public function posts() { return $this->morphedByMany(Post::class, 'taggable'); } public function videos() { return $this->morphedByMany(Video::class, 'taggable'); } }
Or use it to merge relationships with different depths:
class User extends Model { public function allComments() { // TODO } public function comments() { return $this->hasMany(Comment::class); } public function postComments() { return $this->hasManyThrough(Comment::class, Post::class); } }
Step 1: Creating Views
Before you can define the new relationship, you need to create the merge view in a migration:
use Staudenmeir\LaravelMergedRelations\Facades\Schema; Schema::createMergeView( 'all_taggables', [(new Tag)->posts(), (new Tag)->videos()] );
By default, the view doesn't remove duplicates. Use createMergeViewWithoutDuplicates()
to get unique results:
use Staudenmeir\LaravelMergedRelations\Facades\Schema; Schema::createMergeViewWithoutDuplicates( 'all_comments', [(new User)->comments(), (new User)->postComments()] );
You can also replace an existing view:
use Staudenmeir\LaravelMergedRelations\Facades\Schema; Schema::createOrReplaceMergeView( 'all_comments', [(new User)->comments(), (new User)->postComments()] );
The package includes staudenmeir/laravel-migration-views. You can use its methods to rename and drop views:
use Staudenmeir\LaravelMergedRelations\Facades\Schema; Schema::renameView('all_comments', 'user_comments'); Schema::dropView('all_comments');
If you are using php artisan migrate:fresh
, you can drop all views with --drop-views
.
Step 2: Defining Relationships
With the view created, you can define the merged relationship.
Use the HasMergedRelationships
trait in your model and provide the view name:
class Tag extends Model { use \Staudenmeir\LaravelMergedRelations\Eloquent\HasMergedRelationships; public function allTaggables(): \Staudenmeir\LaravelMergedRelations\Eloquent\Relations\MergedRelation { return $this->mergedRelation('all_taggables'); } }
If all original relationships target the same related model, you can use mergedRelationWithModel()
. This allows you to
access local scopes and use methods like whereHas()
or withCount()
:
class User extends Model { use \Staudenmeir\LaravelMergedRelations\Eloquent\HasMergedRelationships; public function allComments(): \Staudenmeir\LaravelMergedRelations\Eloquent\Relations\MergedRelation { return $this->mergedRelationWithModel(Comment::class, 'all_comments'); } }
You can use the merged relationship like any other relationship:
$taggables = Tag::find($id)->allTaggables()->latest()->paginate(); $users = User::with('allComments')->get();
Pivot Table Data
You can retrieve additional pivot columns if your merge view consists of many-to-many relationships.
Add the desired pivot columns to all relationships:
use Staudenmeir\LaravelMergedRelations\Facades\Schema; Schema::createMergeView( 'all_taggables', [ (new Tag)->posts()->withPivot('tagged_at'), (new Tag)->videos()->withPivot('tagged_at'), ] ); $taggables = Tag::find($id)->allTaggables; foreach ($taggables as $taggable) { dump($taggable->pivot->tagged_at); }
Limitations
In the original relationships, it's currently not possible to limit the selected columns or apply withCount()
.
In the merged relationships, it's not possible to remove global scopes like SoftDeletes
. They can only be removed in
the original relationships.
Testing
If you use PHPUnit or a similar tool to run tests, add this property to your base test class to ensure that database views are dropped when the test database is cleaned up:
protected bool $dropViews = true;
Contributing
Please see CONTRIBUTING and CODE OF CONDUCT for details.