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 Callbacks Laravel Package

ganyicz/nova-callbacks

Define Eloquent model event callbacks such as creating, updating, and deleting directly inside your Laravel Nova resources. Keeps event logic close to related fields and removes the need for separate observers.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What this package actually is. Roughly 100 lines of glue that hook Nova's resource store()/update()/destroy() flow and invoke named methods (afterCreate, afterUpdate, etc.) on the resource class with the affected model and NovaRequest. It adds no schema, no config, no routes, no assets. The scope is small enough that the source is more authoritative than the README, and small enough that the real question is not "does it work" but "who owns this code in two years."

Architecture fit. The package makes an implicit architectural bet: post-save side effects belong in the presentation layer (the Nova resource), not in model observers, domain events, or services. That is correct for a narrow and legitimate class of behavior — audit rows stamped with the acting admin, admin-scoped notifications, cache busts that only matter when a human edits a record. It is wrong for anything an API endpoint, console command, queue worker, or CSV import must also trigger. Adopting this package without drawing that line explicitly bakes a "why didn't this fire?" bug class into the codebase. Fit is therefore conditional: high for a small internal admin tool where Nova is the only write path; near-zero for a system where Nova is one of several mutation surfaces.

Integration feasibility. Installation is one composer require and a method on a resource class. The real friction is not technical, it is procurement and CI:

  • Nova is paid and closed-source. Local dev and CI both need NOVA_USERNAME/NOVA_PASSWORD. Any team that has not already automated this will spend more time on CI secrets than on the package.
  • The 2022 release means the dependency must resolve against a Nova major that did not exist when it was written, or against a pin we no longer run. A clean composer require proves the constraint graph is satisfiable — it proves nothing about whether the callbacks still fire. That is a test, not a resolve.

Technical risk. Ranked by expected cost:

Risk Likelihood Impact Mitigation
Silent breakage on Nova major upgrade High High — missing side effects on production writes, not a build error Canary test per resource with a callback
Bulk paths (Actions, Lenses, CSV import) bypass the hooks High Medium — rows differ depending on how they were created Test the import path explicitly; keep business rules out
Method-override collisions with other Nova extensions wrapping store/update Medium Medium — ordering depends on trait bootstrap order Test combinations; limit to one such package per resource
No idempotency → duplicate side effects on user retry Medium Medium Make every callback body idempotent or a single dispatch()
Single maintainer abandons it Already true Medium Fork-and-vendor plan from day one
Nova core ships equivalent lifecycle hooks Unknown Would make the dependency redundant Check the Nova changelog before committing

Key questions to answer before any commitment.

  1. Which Nova majors does composer.json permit, and does it resolve against our current pin today?
  2. Do callbacks fire on create, update, delete, soft delete, force delete — and for inline-created related records?
  3. Is the save plus callback wrapped in a transaction? This determines whether ->afterCommit() is required on dispatched jobs.
  4. Does our Nova version already ship first-party lifecycle hooks that make this redundant?
  5. Do other installed Nova extensions also wrap store/update?
  6. What is the actual argument order and method signature on the installed version? (cat vendor/ganyicz/nova-callbacks/src/*.php — five-minute read, more reliable than the README.)

Technical evaluation bottom line. Small, well-shaped, low blast radius, and effectively unmaintained. Score 0.47 with 20 stars, zero dependents, and no release since June 2022 is the correct read: this is a pattern worth copying, not a dependency worth carrying. The capability is hours of work to replicate; the dependency is an unowned component in the admin write path.

Integration Approach

Stack fit. Laravel 8/9-era, PHP 8.x, Nova 3/4-era. It fits cleanly if we are on a contemporaneous Nova major; it fits with unknown risk if we are on anything newer. The stack question that actually gates us is not Laravel — it is which Nova major we pin and whether we can run it in CI with credentials.

Compatibility policy: pin two dimensions. Nova major × Laravel major. Treat every Nova upgrade as a breaking change for this package specifically, regardless of what the changelog says. The failure mode is silent (side effects stop firing, saves still succeed), so the upgrade runbook entry needs a named owner and a re-validation step, not a note to "check things still work."

Migration path — abstract first, adopt second. Do not sprinkle package-specific method names across resource classes. Define one contract and implement it in a base resource:

abstract class Resource extends NovaResource   // the only file that knows about the package

This is the difference between a one-file exit and a fifty-file mechanical edit. If we skip it, removal cost scales with the number of resources that ever touched a callback — which is exactly the number that will be largest when we need to remove it.

Sequencing. The order matters because each step is cheap and each can terminate the effort:

  1. Verify resolution against the pinned Nova major. Abort on constraint conflict — do not force it.
  2. Write the failing test first, on the lowest-risk resource we own. Assert the side effect through the Nova HTTP layer (actingAs + PUT /nova-api/...), never by calling the method directly. A unit test of afterUpdate() proves nothing about whether the package invokes it.
  3. Run it red, add the callback, run it green. If it never goes green, we have learned the package is dead on our Nova version for the cost of an afternoon. That is the cheapest possible outcome and it is a legitimate way to spend the afternoon.
  4. Spike one real workflow end to end — audit trail is the canonical choice: no external I/O, safe under retry, obvious business value.
  5. Decide: adopt behind the interface, or inline. Default recommendation is inline. Callbacks are ~100 lines of store()/update() wrapping; we are renting ergonomics, not capability.
  6. If adopted in the interim, schedule removal against the next Nova major rather than discovering it mid-upgrade. Fork-and-vendor (copy source into app/Support/, delete the composer requirement) is the realistic end state, not a fallback.

Where the callbacks must route shared logic. Use a trait for behavior that recurs ("stamp updated_by, write an audit row"), mixed into resources that opt in. This also gives us the seam to swap the implementation when we inline.

Non-negotiable code-review rule. A callback body may do exactly two things: write to the same database, or dispatch() a queued job. Nothing else. External HTTP, mail, Slack, CRM sync — all of it goes behind a queue with ->afterCommit(), or a failing third-party endpoint 500s the admin's save.

Operational Impact

Maintenance. Single maintainer, no release in ~3 years, zero dependents. We would be the only downstream consumer, which means we are the only ones who notice when it breaks and there is no community backpressure to produce a fix. Budget this as an owned fork, not a dependency with an upstream. The failure is not a red build; it is an audit row that quietly stops being written on a Tuesday.

Support. There are no logging or retry hooks in the package. If a callback throws, the only trace is an application exception and whatever the user saw. Wrap bodies in try/catch with Log::error(), or — better — keep the body to a single dispatch() and let the queue give us retries and a failed_jobs record for free. Do not let a Slack webhook 500 the admin's save.

Scaling. Callbacks execute inline in the Nova HTTP request, after the DB write, with no transaction and no retry. Every callback is added latency on every admin save. Trivial for an audit insert; unacceptable for anything that talks to a network. The scaling rule is the same as the review rule: write locally or dispatch. Anything that grows in duration must move to the queue before it grows in volume.

Failure modes to plan for.

  • Post-commit failure window. Save succeeds → callback throws → user sees 500 → user saves again → side effect runs twice. Mitigation: idempotent bodies (ShouldBeUnique jobs, upsert on a natural key, Cache::lock on model + action).
  • Bulk-path bypass. Nova Actions, Lenses, and the CSV import card do not go through resource store/update. Records created by import or mass action will silently lack side effects. This is the highest-probability source of "why is this row different?" production bugs. Test the import path specifically.
  • Trait ordering collisions. With other Nova extensions wrapping store/update (redirect-after-save, tabs), execution order is whichever bootstraps first. That is not a bug to debug from a support ticket. Test the combination before shipping it.
  • Delete semantics. Confirm from source whether the delete callback fires on soft delete, force delete, or both, and whether $model is still readable at that point. Do not assume.
  • Business-rule drift. If a non-admin code path creates or mutates the record, the callback is the wrong home. Anything authoritative belongs in a model observer, domain event, or service.

Ramp-up. Engineering: negligible — reading the source is five minutes, and the first callback is an afternoon including the failing test. The real enablement cost is elsewhere:

  • Convention work — deciding and documenting callback-vs-observer, and where the boundary sits. This is the expensive part and it does not go away if we skip the package.
  • CI credentials for the Nova license, if not already automated.
  • Runbook entry for the Nova upgrade, with a named owner and the canary-test step spelled out.
  • Code review enforcement of the dispatch-don't-do rule and the interface boundary.

Budget an enablement effort, not a spike. The spike is cheap; the convention is what we are actually buying, and we can buy it without the dependency.

Recommendation. Do not adopt as a production dependency. Use it as a reference implementation and a one-afternoon spike to make the requirement concrete and demoable, port the pattern into a first-party trait we control, and put the result behind an interface so the seam exists regardless. If it is already in the codebase, treat the finding as a maintenance signal: inline it on the next Nova upgrade, scheduled with an owner, rather than patching around it.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package