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

Dynamic Action Fields Laravel Package

tunezilla/dynamic-action-fields

Laravel package for defining dynamic, configurable fields on actions, letting you build and render action forms at runtime instead of hardcoding inputs. Useful for customizable admin workflows, reusable action classes, and flexible user-defined input handling.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pin. composer require tunezilla/dynamic-action-fields, then immediately open composer.json and tighten the constraint to the exact version you tested. Nova is paid and closed-source; this package is effectively single-maintainer with zero dependents, so you are the canary. Record the Nova version alongside the package version in the PR description — treat them as one dependency.

Read the trait before you write any code. The whole integration surface is small (an afternoon's read). Open the package's src/ first and answer four questions in writing before touching an action:

  1. The exact trait (or base class) name, namespace, and the hook signature your fields() moves into.
  2. Whether field resolution fires once at modal open against the request payload, or reactively as sibling fields change. These are very different packages; the cascading use case depends entirely on which is true.
  3. Whether validation (rules()) flows through the same dynamic path as rendering.
  4. Whether the trait exposes the selected resources / $this->resource, so you can seed defaults from the checked rows.

First use case: one low-risk action, conditionally. Pick an action where a type or reason_code select should surface one extra field and hide several irrelevant ones. Keep the existing static fields() implementation intact behind the trait so rollback is a one-line revert. Do not refactor a fleet of actions in the first pass — the point of the first conversion is a real go/no-go gate.

Write the smoke test in the same PR. A test that opens the action modal as an authorized user and asserts the returned field set is the highest-leverage artifact in the entire integration. Nova's closed source means this test is your only reliable compatibility signal on a Nova bump.

Implementation Patterns

Trait-on-action, fields-into-hook. The mental model is "move fields() into a callback that re-runs against the live request." Existing field objects (Select::make(...), Text::make(...)) stay as they are — you are changing when the array is built, not what's in it. Verify the exact names against source; the shape is roughly:

use Tunezilla\DynamicActionFields\Concerns\ResolvesDynamicFields; // verify namespace

class IssueRefund extends Action
{
    use ResolvesDynamicFields;

    public function fields(NovaRequest $request): array
    {
        // called each time the modal is serialized
        return [
            Select::make('Method', 'method')->options([...]),
            // conditional branch off a sibling value / selected rows
        ];
    }
}

Branch on the three useful contexts. In practice the branching inputs are: the selected resources ($this->resources / the models themselves), the acting user ($request->user() — role, tenant, permissions), and record state (order status, currency, whether the row is already refunded). Reach for these directly in the resolver rather than encoding variants as separate action classes. That consolidation — one action instead of five near-duplicates — is the actual day-to-day payoff, not conditional rendering for its own sake.

Dependent selects. For a country → region or category → subcategory pair, resolve the child's options() from the parent's value in the same pass. Confirm whether that works at modal-open time only (parent pre-selected from the resource) or live (parent changes inside the modal). If it's open-time only, design around it: seed the parent from the selected rows so the dependent field is correct on first paint, and accept that in-modal edits won't cascade.

Keep validation co-located with rendering. Derive rules() from the same condition function as fields(). The clean pattern is a private method that returns a context object or a matched variant key, consumed by both:

private function variant(NovaRequest $request): string { /* ... */ }

public function fields(NovaRequest $request): array { return match($this->variant($request)) { ... }; }
public function rules(NovaRequest $request): array { return match($this->variant($request)) { ... }; }

One source of truth for "which variant are we in" is what prevents required-but-hidden fields and, worse, values that pass unvalidated on a data-mutating admin action.

Coexistence and composition. Check whether dynamic fields double-serialize when combined with Nova's native dependsOn / withMeta. Pick one mechanism per action rather than mixing them — mixed strategies are the source of "the modal shows the field but the payload doesn't contain it" bugs. Same discipline for withMeta(): if you attach meta conditionally, make sure the meta resolver runs in the same pass as the fields.

One base class per team. Put the trait on a single project-owned abstract DynamicAction base class rather than sprinkling use across action classes. Then a swap to a vendored fork, an inline copy, or a hand-rolled replacement is mechanical instead of a hunt. Keep a short inventory file (action class → what it resolves against → why) in the repo; it doubles as the migration checklist for a Nova upgrade and the onboarding doc.

Gotchas and Tips

  • Nova version drift is the dominant risk, not code. No downstream canary, no external bug pressure forcing the maintainer to keep pace. Pin package and Nova minor in a joint constraint window, and treat a Nova upgrade as a coordinated change requiring re-verification of every converted action.
  • Assume nothing about resolution timing until you've tested it. If you need live reactivity inside the modal and the package is one-shot, the cascade use case evaporates and the dependency may not be worth carrying. Test this in the first hour of the spike, not after converting three actions.
  • Validation parity is a correctness blocker, not cosmetics. If rules are re-derived and can diverge from the rendered set, you can submit an action that mutates data with a hidden-but-required field missing. Verify against a variant where a required field is hidden before you ship anything that writes.
  • The resolver runs per modal open — and possibly per field change. Never put queries without eager loading or memoization in it. $this->resources->load('customer.tenant') inside the resolver, called repeatedly, is a fast path to a slow modal. Resolve once into a local property and branch off that.
  • Don't mutate shared field instances. Build fresh field objects per resolution. Mutating a cached Select and returning it works in dev and produces baffling stale-options bugs across requests.
  • "Required but hidden" is the classic symptom of fields and rules disagreeing. The second symptom is a submitted payload missing a key the field rendered. Add both assertions to the smoke test: the field set and the validation outcome for each variant.
  • Debugging is easier than it looks. Log::debug() the resolved field names plus the branch key inside the hook; you'll see immediately whether the resolver saw what you expected or whether Nova never called it. If the hook doesn't fire at all, suspect the trait isn't applied (or the action isn't using the base class) — not Nova.
  • Budget half a day to vendor or inline. At 18 stars and zero dependents, community support is thin and Stack Overflow coverage is near zero. The realistic plan is: read the source, understand it, and be prepared to own it. That's cheap here because the surface area is genuinely small — but write it into the estimate rather than discovering it during a Nova upgrade.
  • Scope it explicitly to internal tooling. Keep it out of customer-facing flows, both because validation guarantees are unproven and because it compounds any future migration off Nova. If you're planning to move off Nova in the near term, skip the dependency entirely.
  • Stop and reconsider if the variant logic fits native Nova conditional fields, dependsOn, or two separate actions. For a single edge case, the maintenance tax exceeds the gain.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels