phoenix-lib/nova-nested-tree-attach-many
Phoenix Lib's Nova Nested Tree Attach Many adds a Laravel Nova field for selecting and attaching many related records through a nested tree UI. Browse hierarchical data, search nodes, and manage parent-child selections directly from Nova forms.
Architecture fit. The package is a thin Nova field — a presentation layer over a standard BelongsToMany pivot. It ships no schema, no service layer, no migrations, no background jobs. That is the right shape for a TPM decision: adoption does not constrain the data model, and exit cost is a one-line removal from fields(). Fit is narrow but clean: it works when you already run Laravel Nova, the related model is self-referential, and the relationship is many-to-many (multiple parents per node). It is the wrong shape if the tree is modeled with a single parent_id/kalnoy/nestedset adjacency pattern and the product expects one parent per node — the attach-many semantics would be a data-model mismatch, not a configuration issue.
Integration feasibility. Two modes with very different effort:
Technical risk (ranked).
authorizedTo* methods and a global scope. This is a real security surface that the package does not close.laravel-eloquent-query-cache flush-on-save addition means the upgrade is not behaviorally inert. Review the diff and confirm scope.Key questions for engineering before any spike.
composer.json solve at all?BelongsToMany, or a single-parent tree?position/label, that enrichment moves outside Nova.Stack fit. Confirmed only after a ten-minute version gate: open composer.json, check the Nova/PHP/Laravel constraints against our install, then confirm the published dist/js assets actually load in our Nova version. A failed constraint solve or a Vue 2 asset in a Vue 3 Nova is an immediate reject — do not invest in a spike first. Prerequisites that must already exist: working Nova, a self-referential BelongsToMany (e.g. categories() through a category_category pivot), and the related Resource registered.
Migration path. Install pinned, not floating:
composer require phoenix-lib/nova-nested-tree-attach-many:^1.8
php artisan vendor:publish --provider="PhoenixLib\NovaNestedTreeAttachMany\FieldServiceProvider"
Then add the field to the resource's fields() and verify the argument signature in src/NestedTreeAttachMany.php — do not guess from blog snippets. The pragmatic default, given zero dependents and a single maintainer, is to fork at 1.8.0 on day one and pull it via a VCS repository. The source is MIT and tiny; this costs minutes and buys a patch lane for the inevitable Nova upgrade. Never float on dev-master.
Compatibility posture. Two scenarios:
Also confirm the tree storage strategy. The description says "nested tree" but does not guarantee adjacency vs. nested-set. If we already use kalnoy/nestedset, verify the field reads nested-set columns; a mismatch shows up as a flat or half-empty tree.
Sequencing.
authorizedToAttachAny on the related resource; activity logging on the parent. Test that a user cannot see or attach nodes their policy forbids.BelongsToMany + search. If the tree is not a genuine hard requirement, take the zero-dependency option. The tree is a nicety; the dependency is forever.Maintenance. Ownership is permanent but cheap: the surface is a field class plus a Vue component. Pin exact versions, pin the fork, re-run the smoke test on every Nova upgrade. The smoke test should cover, at minimum: attach two nodes → assert pivot rows; detach one → assert full-sync result; and in relation-less mode, a JSON round-trip plus an orphan-pruning assertion. That single test is what stops a Nova upgrade from silently breaking an ops workflow. composer update on a package with a bus factor of roughly one is how an internal admin screen breaks on a Friday.
Support. No vendor, no SLA, no upstream roadmap commitment. Community issues only, answered by one maintainer at best. Ramp-up for a competent Laravel dev is genuinely low — the mental model is "a Nova field over a pivot." Most day-to-day edits happen in three places: the resource's fields(), a global scope on the related resource, and model observers on both sides of the pivot. Reach for the fork only when behavior needs to go beyond that surface.
Scaling. Field performance is our schema's performance. Index parent_id on the related table and both pivot columns before go-live; a self-referential tree without those indexes looks like a package problem when it is a query-plan problem. Test at 500 and 5,000 nodes and watch query log and browser render time. There is no lazy load or server-side search in a 2021-era Vue 2 tree, and relation-less mode with several blocks per form multiplies query count per form load. If we hit the ceiling: scope the tree query to a subtree, or drop back to a searchable BelongsToMany.
Failure modes to instrument for.
dist. Remedy: vendor:publish --force, clear Nova asset cache, hard-refresh, then check the browser console for an "unknown custom element" warning.Ramp-up and exit cost. Half a day to first use, one day to verify, a small permanent maintenance tax thereafter. Exit cost is a single field declaration removal plus reverting the fork — which is precisely why this is a low-regret experiment rather than a platform commitment. Ship it as an incremental admin improvement, measure whether editors actually use tree navigation, and keep the dependency from becoming load-bearing on reporting or search joins. In relation-less mode specifically, remember the trade: JSON columns bypass foreign keys, cascade behavior, and DB-level validation, so any downstream feature that joins on categories will silently miss block-embedded selections — that is the argument for staying relational if those features are planned.
How can I help you explore Laravel packages today?