Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Morph To Create Field Laravel Package

alisaleem27/nova-morph-to-create-field

A Laravel Nova field that adds inline creation to MorphTo relationships. Choose the morph type, fill in the related model's fields, and attach it without leaving the resource form, saving extra navigation and speeding up data entry.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and immediately audit the package. You're pulling in a one-star Nova field with zero dependents, so the install is the least interesting part:

composer require alisaleem27/nova-morph-to-create-field
composer show alisaleem27/nova-morph-to-create-field
ls vendor/alisaleem27/nova-morph-to-create-field/src

Read composer.json's require block first. Confirm it declares a Nova constraint that matches your install (^4.0 vs ^5.0) and that it doesn't drag a second copy of laravel/nova into the tree. Then head -30 every file in src/ — the whole package is small enough to read in a coffee break, and you need the real FQCN because the README's namespace is not trustworthy at this maturity level.

Wire it into the child resource, not the parent. The field only makes sense where the model itself owns a morphTo(). A Comment with commentable() is the canonical case:

// app/Models/Comment.php
public function commentable(): MorphTo
{
    return $this->morphTo();
}
// app/Nova/Comment.php
use AliSaleem27\NovaMorphToCreateField\MorphToCreate; // verify against src/

public function fields(NovaRequest $request)
{
    return [
        MorphToCreate::make('Commentable', 'commentable')->rules('required'),
    ];
}

Establish a morph map before you touch the field. Every failure mode downstream — empty dropdowns, "target resource not found", type validation passing in tinker and failing in Nova — traces back to alias-vs-FQCN drift. Burn this in on day one:

// app/Providers/AppServiceProvider.php
Relation::enforceMorphMap([
    'post'  => \App\Models\Post::class,
    'video' => \App\Models\Video::class,
]);

First use case to pilot: a moderator filing a comment against a post created seconds ago, without abandoning a half-typed comment body to go create the parent. If your operators never create children against freshly created parents, stop here — the package adds nothing.


Implementation Patterns

Start with a create-only field, keep stock MorphTo for updates. Users almost never need to re-parent an existing record, and the update path is where you least want a third-party field's behavior. Split by request:

->fields(function () use ($request) {
    return $request->isCreateOrAttachRequest()
        ? [MorphToCreate::make('Commentable', 'commentable')->rules('required')]
        : [\Laravel\Nova\Fields\MorphTo::make('Commentable', 'commentable')];
})

This contains your blast radius to the create flow, which is the flow you actually bought the package for.

Validate morph types against aliases, server-side, always. Even if the field exposes a type whitelist (grep src/ for types(, relatable(, or similar), don't trust the UI:

MorphToCreate::make('Commentable', 'commentable')
    ->rules('required', Rule::in(['post', 'video']))
    ->creationRules('required');

Those strings are morph aliases under enforceMorphMap, not class names. Validating on \App\Models\Post::class will look correct and then reject every real submission.

Treat authorization as a two-policy problem. Inline creation means the user is creating a Post while creating a Comment. Both policies must hold. Nova checks the Comment policy on the request; whether it checks the Post policy on the inline create depends entirely on how the package resolves its options. If it queries Model::query() directly — a common shortcut in small field packages — it will offer types the user cannot create, and then 403 at submit time with a confusing error. Gate the field itself:

MorphToCreate::make('Commentable', 'commentable')
    ->canSee(fn (NovaRequest $request) => $request->user()->can('create', \App\Models\Post::class))
    ->canSee(fn (NovaRequest $request) => $request->user()->can('update', $request->resource()));

Use it as a create-and-attach shortcut beyond pure morphs. The underlying mechanic — open a modal, POST a new related record, fill the FK — generalizes. In resources where you currently ask users to "go create the parent, then come back and attach it," collapsing that into one form is usually the highest-value change you'll ship that sprint. Just verify the field's fillUsing cycle actually sets the FK before you promise it.

Test persisted state, not the field. Nova's testing helpers cover field visibility and resolution; they do not cover a custom field's creation endpoint. Assert database rows instead:

$this->actingAs($admin)
    ->postJson('/nova-api/comments', [
        'commentable_type' => 'post',
        'commentable_id'   => $post->id,
        'body'             => 'Looks good',
    ])
    ->assertCreated();

$this->assertDatabaseHas('comments', [
    'commentable_type' => 'post',
    'commentable_id'   => $post->id,
]);

Then separately exercise the inline create path: open the Nova UI, watch the network tab, and record the endpoint the field actually calls. Write a test hitting that endpoint directly. It's the only test that proves the package works, and it's the test that will catch your Nova upgrade.


Gotchas and Tips

Verify the namespace before writing a single line. Small field packages routinely ship a namespace that doesn't match their README. composer show plus a look at the top of each src/*.php takes thirty seconds and saves an hour of Class not found.

Morph map drift is failure mode #1. With enforceMorphMap, Eloquent stores commentable_type = 'post'. If the package's internal search compares against App\Models\Post, the dropdown renders empty and you'll waste an afternoon blaming Nova. Grep the package for getMorphClass() and morphMap to see which convention it follows.

Global scopes silently shape your options. Inline search almost certainly runs Model::query(), which applies every global scope — soft deletes, tenant scopes, published-only filters. Soft-deleted parents being hidden is usually correct; a tenant scope hiding a valid parent in a cross-tenant admin context looks like a bug and isn't. Test with a non-default tenant before you ship.

Index views will cost you. MorphTo cannot be eager-loaded in a single query — each morph type is its own whereIn. Fifty comments across two types is three queries, not one. Fine. Two hundred rows across four types, on a page someone refreshes constantly, is not. Keep the field on detail/form screens and check indexQuery behavior before putting the column in a table.

Nova 4 vs Nova 5 is a real cliff. Nova 5 changed field method signatures and resolution semantics. A field built against Nova 4 will happily render and then quietly fail to resolve or fill. Test the create flow end-to-end, not just that the form loads — "the field appears" is not evidence of compatibility.

Debugging is a network-tab sport. The two failure modes are "wrong type string sent" and "creation endpoint 403'd," both visible in under a minute. If you need more, wrap the field in a throwaway ->help() or log payloads via middleware scoped to /nova-api/* for the relevant resources. Don't add debugging to the package itself.

Extension points, in order of preference: fillUsing() on the field instance inside your resource (no package changes), then a subclass overriding resolve/fill, then vendoring. Never edit vendor/ — the next composer install erases it, including on your colleague's machine.

Prefer vendoring over forking over depending. With one star and no release cadence, assume no maintenance. The moment you need a change — a bug fix, a Nova 5 adaptation, an authorization gate the package won't give you — copy the field class and its Vue component into app/Nova/Fields/MorphToCreate.php, point the resource at your copy, and composer remove the package. You lose updates you were never going to receive and gain the ability to fix things on a Friday afternoon. If you're not prepared to do that, don't adopt the package.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib