guava/filament-nested-resources
Filament Nested Resources adds nested resource routing and management to Filament admin panels. Define parent-child resources, generate nested URLs, and keep breadcrumbs and authorization in sync so related records are created and edited in their parent context.
Architecture fit. guava/filament-nested-resources is a Filament v3 plugin that decorates Filament's existing resource layer rather than replacing it. A service provider registers the package, and each child resource opts in individually via a NestedResource trait plus two static declarations — getParentResource() and getParentResourceRelationship() — after which the plugin generates parent-bound routes in the shape /{parent}/{child} (e.g. /projects/{project}/tasks) with auto-generated URLs, slugs, breadcrumbs, and parent-aware record resolution. From a TPM standpoint the decisive property is that adoption is additive and scoped per resource pair, not panel-wide: there is no schema migration, no flag day, and no lock-in at the data layer. A single parent–child pair can be piloted and reversed by removing the trait. That is what makes this a tactical admin-layer bet rather than a platform commitment — which matters given the near-zero adoption signal (0 dependents, unscored stars).
Integration feasibility. Install cost is trivial: Composer, publish the config tag, register the plugin in the panel provider, add the trait, declare the parent resource and Eloquent relationship, set $shouldRegisterNavigation = false on the child. The real effort is information architecture and authorization modelling, both of which are product decisions before they are engineering decisions. Two items drive the estimate. First, every nested pair requires a genuine Eloquent relationship whose name matches the declaration — mismatches fail at runtime, not at review time. Second, every inbound link to a child record now has to carry the parent, so getUrl() call sites scattered across widgets, notifications, custom actions, and global-search result URLs all need auditing. Budget one to two engineer-days for a first production pair, not one hour. The install is not the work; the URL audit and the policy review are.
Technical risk. Moderate-low overall. Release 1.3.0 is not where the risk lives — it is a single breadcrumb improvement with no indicated breaking changes, making the upgrade close to free (smoke-test any resource that overrides getBreadcrumb() or ships a custom panel theme, since overrides are precisely what bypasses new defaults). The structural risks are three. (1) Maintainer concentration: a community plugin with effectively no downstream ecosystem means we will likely be the first to hit regressions rather than the beneficiary of someone else's bug report. (2) Coupling to Filament internals: nested route registration sits close to Filament's resource and route contracts, so Filament majors are the primary breaking vector. (3) The authorization conflation trap: relationship scoping prevents cross-parent access but is not a policy check, and teams routinely assume the former substitutes for the latter.
Key questions.
{parent}/{child} URL shape stable across minor releases? If not, saved bookmarks and outbound notification links break silently.Stack fit. Laravel + Filament v3 only; a non-starter for anything else and explicitly an admin-facing capability. It should not be used to model public-facing nested URLs — that is a separate product and SEO decision. Before code is written, confirm the panel is on v3 and pin the package alongside an explicit Filament constraint in composer.json; an unpinned transitive Filament bump is the most likely way this blows up quietly.
Build vs. buy vs. use what Filament already ships. Frame this as a three-way decision, not two. Filament's built-in RelationManager already covers inline child CRUD and is the correct answer when children only need managing from inside the parent screen. Custom routing is what this package replaces. The decision rule to write down and socialize: adopt the plugin only where children need first-class pages — their own table, filters, custom pages, widgets, or shareable deep links. Applying it to every parent–child pair adds route and breadcrumb surface nobody asked for.
Migration path. Pilot one low-risk, admin-only pair (Course → Lesson, Project → Task) on a staging branch. Register the plugin, add the trait, declare parent resource and relationship, hide the child from navigation, and add a parent-side action linking to the child index. Keep the flat child routes live during transition so existing bookmarks, tests, and external links keep resolving; retire them only after pilot sign-off. Then expand pair by pair behind route prefixes or feature flags, never as a single rollout.
Compatibility. Four gates before pilot sign-off: Filament version alignment, Livewire version alignment, conflicts with custom resource pages or overridden route registration, and behaviour under the panel's existing tenancy configuration.
Sequencing.
getUrl() call sites so no parent parameter is ever omitted.Pilot exit criteria — make these measurable: zero 404s across the nested route set after route:clear plus re-cache; policy checks verified for all four verbs including a second-tenant negative test; breadcrumbs render every ancestor as a link and the current record as plain text across all three levels; no N+1 introduced on the nested index, asserted by query count in a test.
Maintenance. This is the dominant cost, and it is not the package — it is the upgrade tax. Budget an explicit compatibility task per Filament major: review the package's composer.json, run the nested route suite, and be prepared to vendor or fork if the maintainer lags. Pin versions, read changelogs before minor bumps, and treat Filament upgrades as blocked until the plugin is verified. With low ecosystem signal, plan on discovering regressions ourselves rather than inheriting fixes.
Support. No commercial SLA; support is the upstream GitHub repository. That makes a named internal owner mandatory — accountable for the version pin, the nested-resource convention doc, and a thin regression suite covering routing, authorization, tenancy, and breadcrumbs. Without that owner this becomes orphaned infrastructure that silently blocks Filament upgrades. Upstream response time must never sit on the critical path of a product commitment; if the admin panel becomes business-critical, the fork/vendor contingency moves from "plan" to "budget line."
Scaling. Two distinct concerns. Data: nested index pages deepen query chains (child listing filtered by parent, often with ancestor breadcrumb lookups); assert query counts in tests so the convenience does not quietly introduce N+1s as hierarchy depth grows. Surface area: the URL audit does not scale linearly — each new getUrl() call site added after adoption is a latent broken-link bug, so URL generation should be centralized in one helper and that rule enforced in review. Three-level hierarchies are the practical ceiling before breadcrumbs and route names become hard to reason about.
Failure modes. The realistic ones, ranked: silently orphaned child links when a getUrl() caller omits the parent parameter (user-visible 404 or wrong-record access); authorization assumed-but-absent where relationship scoping is mistaken for a policy check; route-cache regressions after deploy that surface only in production; tenant bleed where the plugin does not scope the parent and we assumed it did; and a Filament upgrade that breaks nested route registration with no upstream patch, freezing the panel on an old major.
Ramp-up. Low for any engineer already fluent in Filament conventions — the trait-plus-two-static-methods surface is small and idiomatic. The ramp-up risk is not syntax, it is judgement: engineers must internalize when to nest versus reach for a RelationManager, and that judgement lives in the convention doc, not the package. Onboarding cost is therefore one document plus one reference implementation, both of which should exist before the second resource pair is migrated.
How can I help you explore Laravel packages today?