fidum/laravel-eloquent-morph-to-one
Adds a MorphToOne relationship to Laravel's Eloquent ORM, letting a model own a single polymorphic related record. Useful when a parent has exactly one morph child, mirroring MorphMany but returning a single model with lazy and eager loading support.
Install the package via Composer:
composer require fidum/laravel-eloquent-morph-to-one
The package adds a morphToOne relationship method to Eloquent. Use it on the model that holds the polymorphic foreign key (e.g. commentable_id / commentable_type) when you want a relationship that targets one specific morph type instead of any type.
First use – define a typed relation on a Comment model:
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
public function post()
{
return $this->morphToOne(Post::class, 'commentable');
}
}
Now $comment->post returns the related Post model only when commentable_type is Post::class; otherwise it returns null. This is the package’s core value: type-safe, eager-loadable relations out of a polymorphic column.
Look first at the package’s README for the exact morphToOne signature. The release 2.6.0 adds support for Laravel 13.x and PHP 8.5 – if you are on those versions, use this release or later.
Multiple typed relations on the same morph column
class Comment extends Model
{
public function post()
{
return $this->morphToOne(Post::class, 'commentable');
}
public function video()
{
return $this->morphToOne(Video::class, 'commentable');
}
}
Eager loading
$comments = Comment::with('post')->get();
Only comments whose commentable_type matches Post will have a non-null post.
Query constraints
$comments = Comment::whereHas('post', function ($query) {
$query->where('published', true);
})->get();
Saving through the relation
$comment->post()->associate($post);
$comment->save();
This sets both commentable_id and commentable_type to the Post model. Use dissociate() to clear the relation.
Mixing with standard morphTo
Keep a generic morphTo for cases where you need the mixed type:
public function commentable()
{
return $this->morphTo();
}
Then add typed morphToOne relations for convenience and type-hinting.
morphToOne is provided by this package. Ensure the service provider is auto-discovered (standard for Laravel 5.5+).Relation::enforceMorphMap()), verify the package resolves the mapped type correctly. In most cases it uses the underlying morphTo logic, so maps should work, but test if you rely on them.Post will make video return null. This is expected – a model can only belong to one morph target at a time.null for non-matching types. That’s the intended behaviour; use whereHas if you need to filter only matching records.2.6.0 supports Laravel 13.x and PHP 8.5. If you are on older Laravel/PHP versions, check the package’s composer.json for the supported range – earlier releases may still work.*_type column value ($comment->commentable_type). A common mistake is storing a fully qualified class name while the package expects a morph alias, or vice versa.morphTo with a where clause on the type column.How can I help you explore Laravel packages today?