digital-creative/nova-inline-morph-to
A Laravel Nova field for inline morphTo editing: create and update polymorphic related records directly from the parent resource form, without leaving the page. Streamlines data entry and polymorphic relationship workflows in Nova.
Install the field with Composer alongside Nova:
composer require digital-creative/nova-inline-morph-to
There is no config file or migration step — the package is a single Nova field class plus compiled JS assets, so the fastest way to understand it is to read src/InlineMorphTo.php and the short README example, then copy that example straight into a resource.
The first, most natural use case is a polymorphic "owner" relation on a secondary model: a Comment that belongs to either a Post or a Video, a Note that belongs to a Contact or Company, or an Image that belongs to any number of content types. The field lets the Nova user pick the morph type and create/edit the related model's fields inline, without navigating away to another resource.
Minimal wiring — replace the native Nova MorphTo field on the child resource:
use DigitalCreative\InlineMorphTo\InlineMorphTo;
public function fields(NovaRequest $request)
{
return [
Text::make('Body'),
InlineMorphTo::make('Commentable')
->types([
Post::class,
Video::class,
])
->fields([
Text::make('Title')->rules('required'),
Textarea::make('Body')->rules('required'),
]),
];
}
Start by testing on a simple two-type morph, then expand. Check the package's composer.json for the supported Nova major version before installing; releases track Nova 4/5, so lock to a tag that matches your Nova install.
Type-scoped field sets. Most real-world morphs have different attributes per type (a Post has a title, a Video has a URL). Define separate field arrays per morph class so validation and UI stay honest:
InlineMorphTo::make('Attachable')
->types([
Post::class => [Text::make('Title')],
Video::class => [Text::make('Url'), Text::make('Duration')],
]);
Use this pattern whenever the underlying models diverge; sharing one field list across heterogeneous types leads to confusing validation errors.
Validation belongs on the inline fields, not the parent field. Put ->rules() and ->creationRules() on the nested fields. The parent InlineMorphTo only needs ->required() to force a type selection; conditional rules that reference sibling fields work because Nova submits everything in one request.
Reuse field factories. Because the field list is plain PHP, extract it to a method or a Fields helper class and share it with the child resource:
->fields(PostResource::inlineFields())
This keeps the standalone "create post" page and the inline form in sync — otherwise you will fix a validation rule in one place and forget the other.
Index and detail views. On index/detail the field renders the related resource's title, so ->showOnIndex() / ->onlyOnDetail() behave as expected. Keep it onlyOnForms() for resources where loading the morph on every row is expensive.
Dynamic type lists. ->types() is evaluated per-request, so you can filter by policy or config:
->types(array_filter(
[Post::class, Video::class],
fn ($class) => Gate::allows('create', $class::newModel())
))
This is the cleanest way to avoid presenting types the user cannot create.
Combine with dependency containers. Nested fields are standard Nova fields, so dependsOn() and conditional display inside the inline block work — useful for country/state style fields inside the morph target.
Nova::newResourceFromModel(). An unregistered model produces a blank or broken type.commentable_type and commentable_id in one request; if your model's $fillable or morphTo() name differs from the field's make() argument, the relation silently saves nothing.create policy on that model's resource. Missing policies surface as a generic 403 on submit, not a field-level error — inspect the network tab first.name inside a Post morph on a User resource, for example) unless you deliberately want that hydration.->hideWhenUpdating() and test the update path explicitly — this is the least-covered code path in a 34-star package.composer.json, keep an upgrade test in your suite, and be ready to fall back to a two-field native MorphTo + BelongsTo pair if a Nova upgrade breaks it.vendor/.../dist is being loaded; the compiled assets are not rebuilt by npm in your app. Re-running composer update is usually enough, but a hard php artisan nova:publish clears stale copies.InlineMorphTo and overriding its field resolution is straightforward if you need per-type layouts — but check src/ first rather than forking; the class is small and readable.How can I help you explore Laravel packages today?