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 Nested Tree Attach Many Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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:

  • Relational mode (canonical): half-day. Add the field, run an attach/detach acceptance loop, done.
  • Relation-less mode (1.8.0, beta): 1–2 days plus permanent ownership. No relationship backs the field, so the team owns JSON persistence, shape contract, validation, and orphan pruning. This is the newest, least-specified code path.

Technical risk (ranked).

  1. Nova version lock-in. Last release September 2021, targeting Nova 3 / Vue 2. Nova 4/5 uses Vue 3; the field may not load at all, or requires patching the compiled component. Treat the fork-and-patch as the actual adoption cost, not an edge case.
  2. Bus factor of one. ~35 stars, zero dependents, single maintainer. There is no ecosystem safety net and no support lane. The honest framing to stakeholders is buy the source, own the code.
  3. Silent detach data loss. Attach-many fields differ on full-sync vs. additive save semantics. If save is a full sync, an unchanged re-save can wipe pivot rows. This must be proven against a seed fixture before production — it is the most common quiet failure mode for this class of field.
  4. No built-in authorization or multi-tenancy. The field will happily render nodes a policy forbids. Scoping and permissions live in the related Resource's authorizedTo* methods and a global scope. This is a real security surface that the package does not close.
  5. Whole-tree load. Every form render pays to hydrate and render the full tree; there is no lazy load or server-side search. At 5,000 nodes this becomes an admin UX liability, and relation-less mode with several blocks per form multiplies the cost.
  6. Behavioral change in 1.8.0. The 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.

  • Which Nova / PHP / Laravel versions are we on — does composer.json solve at all?
  • Is the relationship truly multi-parent BelongsToMany, or a single-parent tree?
  • Does the field pass pivot payloads through, or is it an ID-only selector? If we need position/label, that enrichment moves outside Nova.
  • What is the realistic node ceiling in production taxonomy, and does the tree need scoping to a subtree?
  • Who owns the fork, and what is the smoke test?
  • Do we need relation-less mode now, or is it a future "flexible content" requirement we can defer?

Integration Approach

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:

  • We are on Nova 3: compatible, low-friction, proceed.
  • We are on Nova 4+: budget the fork as the real cost. If patching the Vue component to Vue 3 looks like more than a week, writing our own tree-select against our Nova version is a legitimate alternative — and probably a better long-term asset.

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.

  1. Version gate (10 min). Constraint solve + asset load. Hard stop if it fails.
  2. Relational-mode spike (half-day). Seed 10 → 500 → a few thousand nodes. Open form, expand, attach two, save, reopen, detach one, re-save without change. If that loop passes and survives an unchanged re-save, the field is proven.
  3. Detach-semantics regression test. Explicitly assert full-sync vs. additive behavior on the pivot. This test stays in the fork.
  4. Scope and authorization wiring. Global scope on the tree query for tenant; authorizedToAttachAny on the related resource; activity logging on the parent. Test that a user cannot see or attach nodes their policy forbids.
  5. Comparison branch. Stand up the same screen with core Nova 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.
  6. Relation-less mode — only if flexible-content-style page builders are actually on the roadmap. Treat as beta: define the JSON shape contract once (flat IDs recommended), add a custom validation rule that every ID exists, and add read-time orphan pruning or a scheduled cleanup.
  7. Fork, pin, ship. Deploy behind the standard field declaration so rollback is a one-line revert.

Operational Impact

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.

  • Blank field, no PHP error — stale published assets vs. dist. Remedy: vendor:publish --force, clear Nova asset cache, hard-refresh, then check the browser console for an "unknown custom element" warning.
  • Silent categorization loss — full-sync detach semantics on an unchanged save. This is the highest-impact failure mode and must be regression-tested, with pivot backups retained until semantics are proven.
  • Broken category references in relation-less mode — nothing prunes deleted IDs out of a JSON column. Add a read-time filter or scheduled cleanup; otherwise pages render broken references months later.
  • Authorization gap — the field renders nodes the policy says the user cannot attach. Test explicitly; the package will not save us.
  • Cache staleness — the 1.8.0 query-cache flush helps but changes save-time behavior on touched models. Confirm invalidation stays scoped to affected models.
  • Mistyped relationship name — Nova renders nothing. Confirm the field's relation argument matches the model method exactly before escalating as a package bug.

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.

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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus