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

Closure Table Laravel Package

franzose/closure-table

Closure table implementation for Laravel that turns flat tables into efficient hierarchical trees. Query ancestors and descendants in one call, move nodes without rebuilding the tree, and keep parent-child relationships fast, simple, and scalable.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Implements the Closure Table pattern (a separate ancestor/descendant/depth table) layered over an existing adjacency-list parent_id column — the classic hybrid that keeps simple parent lookups cheap while making descendant/ancestor queries a single indexed join.
  • Fits Laravel idiomatically: Eloquent traits and a base node model, Artisan generators for migrations, and relationship-style accessors (descendants, ancestors, children, parent) plus tree-mutating methods (make child of / sibling of / move).
  • Best suited to read-heavy, write-light hierarchies: category trees, org charts, comment threads, menu structures, nested taxonomies. It is a poor fit for high-churn trees (frequent re-parenting, real-time collaboration) because moves rewrite O(subtree × ancestors) rows.
  • Coupling risk: models typically inherit from or mix in package classes, so the domain model becomes dependent on the package's contract. Keep the tree concern in a dedicated model or a repository boundary rather than spreading the trait across the aggregate root.

Integration feasibility

  • Low-friction install: Composer package, MIT license, no exotic runtime dependencies beyond Laravel's Eloquent/Query Builder. No infrastructure to provision — it is schema + PHP.
  • Requires a migration (generated via Artisan) and a backfill/rebuild step for any pre-existing adjacency data. Confirm whether a rebuild command exists and whether it is safe to re-run in production; if not, plan a one-off backfill script.
  • 462 stars but 0 dependents is the notable signal: no public package ecosystem depends on it, so there are no battle-tested integration patterns to copy. Every integration is bespoke and must be validated in-house.
  • Confirm framework/PHP support matrix before committing — the stated last release (2026-02-01) implies active maintenance, but verify the CHANGELOG and composer.json constraints against the target Laravel version (11/12) and PHP 8.2+.

Technical risk

  • Bus factor / longevity (medium-high): single-maintainer package; viable today but needs an exit plan (fork rights are clean under MIT; the schema is portable, which limits lock-in to the PHP layer).
  • Write amplification (medium): inserts/deletes/moves are multi-row operations. Without transactions and row locking on the affected subtree, concurrent moves can produce depth drift or orphaned closure rows.
  • Consistency (medium): correctness depends on all mutations going through the package. Raw SQL, bulk updates, seeders, or admin tools that bypass model events will silently corrupt the closure table; a periodic integrity check is advisable.
  • Scale ceilings (medium): closure table size grows as nodes × average ancestor count — fine for shallow taxonomies, punishing at depth 10+ or with millions of nodes. Needs a benchmark on realistic data.
  • Data-integrity gaps to check: soft deletes, multi-tenancy scoping of the closure table, UUID/ULID primary keys, polymorphic/multi-tree support in one table.

Key questions

  1. Which Laravel and PHP versions are officially supported, and what is the maintainer's release cadence over the last 24 months?
  2. Are move/delete operations wrapped in transactions with locking, and is there documented concurrency guidance?
  3. Is there a supported rebuild/backfill command for importing an existing parent_id tree?
  4. How does it handle soft deletes, multi-tenancy, and non-integer keys?
  5. What are the measured read and write costs at our expected node count and depth (p50/p95)?
  6. Who owns the fork if upstream goes dormant?

Integration Approach

Stack fit

  • Composer-installable into any Laravel app; no queue, cache, or external service required. Works on MySQL, PostgreSQL, and SQLite, though behavior and index strategy should be validated on the production engine (partial indexes, ON DELETE semantics, and lock behavior differ).
  • Complements rather than replaces existing Eloquent relationships: keep parent_id on the entity table for cheap parent lookups and let the closure table serve subtree queries.
  • If the application is multi-tenant, the closure table must be scoped by tenant (or partitioned per tenant) — decide this before the first migration, since retrofitting a scope means rebuilding the table.

Migration path

  1. Spike (1–2 days): branch-only proof of concept on one real hierarchy; generate migrations, wire the trait, load a production-shaped dataset.
  2. Schema migration: add the closure table alongside the existing parent_id; do not remove any legacy columns.
  3. Backfill: populate closure rows from existing adjacency data; verify with a recursive-CTE or application-level cross-check that ancestor/descendant counts match.
  4. Dual-read verification: run new queries in shadow/compare mode against the legacy traversal logic for one release cycle before cutting over.
  5. Cut over: route mutations through the package; freeze or refactor any bulk SQL that touches the hierarchy.
  6. Cleanup: remove legacy recursive queries and any denormalized path/level columns that are now redundant.

Compatibility

  • Verify the package's Laravel constraint against the app's current major version before scoping; a version gap could force an intermediate upgrade or a vendor fork.
  • Confirm index coverage on (ancestor, depth) and (descendant) for the query shapes actually used; default migrations may need tuning for large tables.
  • Check interaction with existing global scopes, soft deletes, observers, and API-resource serialization — the trait's relationships should not leak closure rows into JSON responses.

Sequencing

  • Do the read-path benchmark before the write-path refactor: if subtree reads are not a measured pain point, the added write complexity is not justified.
  • Land schema + backfill first, mutation routing second, legacy removal last. Keep each step independently revertible; the closure table is additive and can be dropped without data loss if parent_id remains authoritative.

Operational Impact

Maintenance

  • Ongoing cost is low in code volume but non-zero in vigilance: the team owns integrity of the closure table, not just the package. Add a scheduled integrity job (or a nightly check) comparing closure rows against parent_id and alerting on drift.
  • Single-maintainer dependency implies an explicit contingency: pin the version, track upstream releases, and keep a fork-ready posture (MIT permits it; vendoring is straightforward since the surface area is small).
  • Upgrades are the risk moment — the package touches schema and model behavior, so treat minor bumps as requiring a migration dry-run in staging.

Support

  • Community-only support (issues/PRs). No vendor SLA. Internal ownership should sit with the backend team that owns the domain model; document the closure-table invariants in the service's runbook so on-call engineers aren't reverse-engineering them during an incident.
  • Budget a small internal knowledge artifact: how to rebuild, how to validate, how to safely move a node.

Scaling

  • Reads scale well: subtree and ancestor queries are index-driven joins rather than recursive traversal, which typically outperforms recursive CTEs and repeated application-level walks at moderate depth.
  • Writes scale poorly by design: creating a node at depth d writes d+1 rows; moving a subtree writes |subtree| × |ancestors| rows. Model the row count before launch: nodes × average depth is the table size, and it can be an order of magnitude larger than the entity table.
  • Watch for table bloat and index maintenance cost on the closure table under heavy churn; partition or archive if churn is high.
  • Hot-spotting: concurrent moves within the same subtree contend on the same rows. If the product supports rapid reordering, consider serializing those operations per tree.

Failure modes

  • Silent corruption: any write path bypassing the package (raw SQL, bulk import, admin console, seeder) leaves stale closure rows → wrong subtree results, no error raised.
  • Concurrent move races: interleaved moves without locking produce cycles or depth drift; the tree can become unreadable or infinitely recursive.
  • Depth explosion: pathological depth (long comment threads, deep org charts) inflates the closure table and slows all mutations.
  • Migration collision: generated migration filenames/timestamps and table names may clash with existing schema in a mature codebase.
  • Abandonment: upstream stops shipping; a Laravel major upgrade then blocks the app until the package is forked or replaced.
  • Query leakage: closure relationships surfacing in serialized API payloads or N+1 loops if not eager-loaded deliberately.

Ramp-up

  • For a Laravel-proficient engineer: ~0.5 day to grasp the closure-table concept and the package's API; ~2–3 days to ship a first integration with backfill and tests.
  • For the wider team: ~1 hour of shared context on the invariant ("closure rows must mirror parent_id") and the rule that hierarchy mutations go through the model. This single norm prevents most incidents.
  • TPM-level effort estimate: 1 sprint for a contained pilot (single hierarchy, staging benchmark, rollback plan), 2–3 sprints to migrate all hierarchy consumers and decommission legacy traversal code.

Recommendation signal: worth a scoped pilot if subtree queries are a demonstrated performance or complexity problem and the hierarchy is read-heavy with shallow-to-moderate depth. If hierarchy writes are frequent or depth is unbounded, evaluate a materialized-path or nested-set alternative — the operational burden here lands on write-time integrity, which is the hardest thing to monitor.

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