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 Field Manager Laravel Package

reedware/nova-field-manager

A Laravel Nova package for managing custom fields: register, organize, and reuse field configurations across your Nova resources to keep forms consistent and reduce repetitive field definitions in your admin panel.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require reedware/nova-field-manager

This is a Nova 3.x-era package (last release June 2021), so confirm your laravel/nova constraint before committing. The package is tiny — read src/ and composer.json before you install it into anything production-facing; the whole thing is likely one service provider, one facade, and one manager class.

Registration

Check composer.json for extra.laravel.providers. If auto-discovery is wired up, nothing else is needed. If not, add the provider manually:

// config/app.php
'providers' => [
    // ...
    Reedware\NovaFieldManager\FieldManagerServiceProvider::class,
],

If the provider ships a config file, publish it and read it top-to-bottom — for a package this small, the config is the documentation:

php artisan vendor:publish --provider="Reedware\NovaFieldManager\FieldManagerServiceProvider"

Where to look first

  1. The facade class — note the exact FQCN and root accessor (getFacadeAccessor()), because that tells you whether you're resolving a singleton manager or building fields statically.
  2. The manager class — look for a $fields / $aliases array. That mapping is the whole package.
  3. README.md — for a zero-star package, the README is often the only usage doc.

First use case

The point of the package is to stop scattering use Laravel\Nova\Fields\Text; imports across dozens of resources and instead reference fields through one facade. Start by converting a single resource:

use Reedware\NovaFieldManager\Facades\Field;

public function fields(Request $request)
{
    return [
        Field::text('Name')->sortable()->rules('required'),
        Field::textarea('Notes')->alwaysShow(),
        Field::belongsTo('User', \App\Nova\User::class),
    ];
}

Do it one resource at a time and diff the generated field JSON in the browser network tab — that's the fastest way to confirm the facade produces the exact same field objects as the direct classes.

Implementation Patterns

1. Config-driven / dynamic field sets

This is the pattern that actually justifies the facade. If your resources are generated from config, a schema table, or a tenant-specific field list, you need to map a string to a field class. Nova's own fields don't do that cleanly; the manager does:

// config/forms.php
return [
    'fields' => [
        ['type' => 'text',     'attribute' => 'name',  'label' => 'Name'],
        ['type' => 'currency', 'attribute' => 'price', 'label' => 'Price'],
    ],
];
public function fields(Request $request)
{
    return collect(config('forms.fields'))
        ->map(fn ($def) => Field::{$def['type']}($def['label'], $def['attribute']))
        ->all();
}

2. Centralized alias map for custom fields

If you have in-house fields (App\Nova\Fields\Money, App\Nova\Fields\Address), register them under short aliases once in a service provider rather than importing FQCNs into every resource:

public function boot()
{
    Field::alias('money', \App\Nova\Fields\Money::class);
    Field::alias('address', \App\Nova\Fields\Address::class);
}

Now a rename of a field class is a one-line change, not a project-wide find-and-replace.

3. Wrap it in a thin app-level builder

Facade calls are static and IDE-hostile. Wrap them so the rest of the codebase keeps type hints and autocomplete:

namespace App\Nova\Support;

class Fields
{
    public static function make(string $type, string $label, ?string $attribute = null): Field
    {
        return \Reedware\NovaFieldManager\Facades\Field::{$type}($label, $attribute);
    }
}

Every resource depends on App\Nova\Support\Fields instead of the vendor facade — so if you swap or drop the package later, you edit one file.

4. Use it in actions, lenses, and filters too

Anything that declares fields (Lenses\*, Filters\*, Nova\Metrics value cards, action fields()) can use the facade. Consistency across these is where the payoff compounds.

5. Testing

Nova resource tests are cheap here because the facade is a pure factory. Assert the field types returned:

$fields = (new User)->fields(Request::create('/'));
$this->assertInstanceOf(\Laravel\Nova\Fields\Text::class, $fields[0]);

For dynamic resources, this is the single highest-value test you can write — it catches a bad config key before it 500s in production.

6. Integration with nova-forms / generated resources

If you use Nova's Resource generator or an artisan command that scaffolds resources, make the stub emit facade calls rather than imports. Then every generated resource is already decoupled from field namespaces.

Gotchas and Tips

Staleness is the real risk. Last release 2021-06-13, zero stars, zero dependents. That's Nova 3.x timing. Nova 4 changed the field API meaningfully (method signatures, fields() behavior, NovaRequest typing). Test on Nova 4/5 before adopting; if it breaks, pin laravel/nova below the breaking major or — more likely — just don't adopt it and copy the ~50 lines of manager logic into your app. For a package this small, vendoring is often the better trade than taking a dependency with no maintenance signal.

Facade statics kill static analysis. Field::text() will be flagged by PHPStan/Psalm and won't autocomplete. Add a @method docblock to your wrapper class, or generate one with barryvdh/laravel-ide-helper. Otherwise your team will stop using it after day two.

Watch for memoization / shared field instances. If the manager caches resolved fields (a natural "manager" implementation), you can end up handing the same Field object to two resources. Nova fields are mutable (->rules(), ->sortable(), ->hideFromIndex() all mutate $this), so shared instances leak configuration across resources and across requests under Octane. Verify each call returns a fresh instance:

$a = Field::text('A');
$b = Field::text('A');
var_dump($a === $b); // must be false

Don't shadow Nova's base class. There's already Laravel\Nova\Fields\Field. Importing both the facade and the base class in the same file causes confusing "cannot redeclare" or wrong-type bugs. Alias your import explicitly:

use Laravel\Nova\Fields\Field as NovaField;
use Reedware\NovaFieldManager\Facades\Field;

Argument-order mismatch with Nova. Nova's make() is make($name, $attribute = null, $resolveCallback = null), but many fields have positional quirks (Select::make($name, $attribute)->options(), BelongsTo::make($name, $resource)). If the facade proxies via __callStatic to new $class(...$args), it inherits those quirks — fine — but if it has its own signature, verify label/attribute order matches. A swapped label/attribute produces subtly wrong column headers, not an error.

Unknown aliases fail late. With string-driven field types, a typo ('currnecy') blows up at request time, not boot time. Add a startup assertion:

foreach (array_keys(config('forms.fields')) as $type) {
    abort_unless(Field::has($type), 500, "Unknown field type [$type]");
}

Or better, validate the config in a test.

Config caching. If aliases live in config/nova-field-manager.php, remember php artisan config:cache after edits — a classic "my alias isn't registered" red herring in staging.

Debugging tips.

  • Field::getFacadeRoot() dumps the manager; dd() the alias map to see exactly what's registered.
  • If nothing resolves, check the provider is actually loaded: php artisan about or app()->getLoadedProviders().
  • Compare against a known-good resource using direct imports — if the facade version renders differently, the proxy is dropping fluent calls (some __call proxies don't return $this).

Extension points. The provider's boot()/register() is where you add aliases and, if available, swap the manager binding with your own subclass. That's the cleanest place to add cross-cutting behavior — e.g., logging every field resolution in local, or appending default ->help() text to every field of a given type.

Verdict for day-to-day use. Worth it only if you genuinely need string→field-type resolution (config-driven or multi-tenant schemas). If you're just avoiding imports, a use statement is cheaper than an unmaintained dependency — and if you do adopt it, wrap the facade in App\Nova\Support\Fields on day one so removal is a one-file change.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console