pindab0ter / constrained-morph-to-for-laravel
Constrained polymorphic relationships for Laravel Eloquent - limit morphTo relationships to specific model types
Fund package maintenance!
pindab0ter
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/pindab0ter/constrained-morph-to-for-laravel
Requires
- php: ^8.2
- illuminate/contracts: ^10.48.25||^11.16||^12.0
Requires (Dev)
- larastan/larastan: ^2.0||^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^7.0||^8.8
- orchestra/canvas: ^8.11.6||^9.0||^10.0
- orchestra/testbench: ^8.0||^9.0||^10.0
- pestphp/pest: ^2.0||^3.0||^4.0
- pestphp/pest-plugin-arch: ^2.0||^3.0||^4.0
- pestphp/pest-plugin-laravel: ^2.0||^3.0||^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^1.0||^2.0
- phpstan/phpstan-phpunit: ^1.0||^2.0
- spatie/laravel-ray: ^1.35
README
This package provides type-constrained polymorphic relationships for Laravel Eloquent. It extends Laravel’s morphTo relationship to only accept specific model types, ensuring data integrity and type safety for your polymorphic relations.
The Problem
Laravel’s polymorphic relationships are flexible - a single relationship can relate to multiple model types. But sometimes you want the flexibility of a polymorphic database structure while enforcing that certain relationships only accept specific types:
// Standard Laravel morphTo - returns ANY model type public function commentable() { return $this->morphTo(); } $comment->commentable; // Returns any model type
The Solution
This package lets you constrain polymorphic relationships to specific types:
use pindab0ter\ConstrainedMorphtoForLaravel\HasConstrainedMorphTo; class Comment extends Model { use HasConstrainedMorphTo; /** @return ConstrainedMorphTo<Post, $this> */ public function post() { return $this->constrainedMorphTo(Post::class, 'commentable_type', 'commentable_id'); } /** @return ConstrainedMorphTo<Post|Video, $this> */ public function commentable() { return $this->constrainedMorphTo( [Post::class, Video::class], // Accept multiple types 'commentable_type', 'commentable_id' ); } } $comment->post; // Returns a Post if the type matches, null if it doesn't $comment->commentable; // Returns a Post or Video if the type matches, null otherwise
Requirements
- PHP 8.2+
- Laravel 10.48+, 11.x, or 12.x
Installation
Install the package via Composer:
composer require pindab0ter/constrained-morph-to-for-laravel
Usage
Basic Usage
-
Add the
HasConstrainedMorphTotrait to your model:use Illuminate\Database\Eloquent\Model; use pindab0ter\ConstrainedMorphtoForLaravel\HasConstrainedMorphTo; class Comment extends Model { use HasConstrainedMorphTo; /** @return ConstrainedMorphTo<Post, $this> */ public function commentable() { return $this->constrainedMorphTo( Post::class, // Only allow Post models 'commentable_type', // The type column name 'commentable_id' // The ID column name ); } }
-
When the relationship type matches, it works like normal
morphTo:$post = Post::create([...]); $comment = Comment::create([ 'commentable_id' => $post->id, 'commentable_type' => Post::class, ]); $comment->commentable; // Returns the Post instance
-
When the type doesn't match the constraint, it returns
null:$image = Image::create([...]); $comment = Comment::create([ 'commentable_id' => $image->id, 'commentable_type' => Image::class, // Wrong type! ]); $comment->commentable; // Returns null (constraint not met)
Multiple Allowed Types
You can allow multiple model types in a single relationship by passing an array:
/** @return ConstrainedMorphTo<Post|Video, $this> */ public function commentable() { return $this->constrainedMorphTo( [Post::class, Video::class], // Accept both Post and Video models 'commentable_type', 'commentable_id' ); }
Now the relationship will accept either type:
$post = Post::create([...]); $comment1 = Comment::create([ 'commentable_id' => $post->id, 'commentable_type' => Post::class, ]); $comment1->commentable; // Returns the Post instance $video = Video::create([...]); $comment2 = Comment::create([ 'commentable_id' => $video->id, 'commentable_type' => Video::class, ]); $comment2->commentable; // Returns the Video instance $image = Image::create([...]); $comment3 = Comment::create([ 'commentable_id' => $image->id, 'commentable_type' => Image::class, // Not in allowed types! ]); $comment3->commentable; // Returns null
Advanced Usage
You can optionally specify the relationship name and owner key:
public function commentable() { return $this->constrainedMorphTo( Post::class, // Constrained type (or array of types) 'commentable_type', // Type column 'commentable_id', // ID column 'commentable', // Relationship name (optional) 'id' // Owner key (optional) ); }
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.