toponepercent/baum
Baum is a Nested Set implementation for the Laravel Eloquent ORM, making it easy to store, traverse and query hierarchical data such as categories, menus and trees with efficient single-query reads. Supports Laravel 5.8 through 13.
Architecture fit
parent_id, lft, rgt, depth) on top of Eloquent. It is a read-optimized encoding: ancestor/descendant/subtree queries become single indexed range scans instead of recursive CTEs or N+1 traversals.extend Baum\Node. That is concrete inheritance, not a trait. If the domain already extends an app-level BaseModel, uses UUID/ULID keys, or relies on other packages that also demand base-class inheritance, Baum forces a refactor, a wrapper model, or exclusion. This is the single largest fit decision and should be resolved before any code is written.lft/rgt ranges), so correctness depends on transactional discipline and row-lock behaviour that the package does not advertise. For a collaborative editor with concurrent reordering, this is a design-level risk, not an implementation detail.Integration feasibility
make:baum), one migration adding four integer columns. No service provider configuration, no queue workers, no external services, no published config surface evidenced in the README.lft/rgt invariants break. Any existing ETL or admin bulk-edit path needs auditing.Technical risk
deleting/deleted events for them. Any cache invalidation, search indexing, audit logging, or webhook dispatch attached to model events will silently miss subtree deletions. This is the highest-probability production defect and must be mitigated in application code.Key questions
Stack fit
CategoryTree or MenuRepository, so the rest of the codebase never references Baum\Node directly. That boundary is what makes the dependency reversible.Migration path
composer require toponepercent/baum → php artisan make:baum Category → adjust migration → php artisan migrate. Column names are customizable per model (parentColumn, leftColumn, rightColumn, depthColumn) and should be fixed early, since renaming later touches every query.parent_id only): add nullable lft/rgt/depth, backfill via the package's rebuild routine, then verify invariants (rgt = lft + 1 for leaves, contiguity within scope) before cutting over read paths. Ship the backfill as a one-off command with a dry-run and a row-count assertion.Compatibility
SoftDeletes and the scoped property is supported but subtle; combined soft deletes plus scopes is the most likely source of silent tree corruption and deserves an explicit integration test.Sequencing
Maintenance
Support
Scaling
(scope columns, lft, rgt, depth).scoped column where natural, and treat frequent reordering as a product decision, not a technical one.Failure modes
| Failure | Symptom | Mitigation |
|---|---|---|
Corrupted lft/rgt |
Missing/duplicated subtree results, wrong depth | Rebuild command, CI invariant check, alert on failed assertion |
| Concurrent moves | Deadlocks, lock wait timeouts | Short transactions, retry with backoff, serialize moves per tree |
| Subtree delete skips descendant events | Stale caches, orphaned search documents, audit gaps | Application-level subtree traversal before delete; explicit reindex job |
| Bulk import bypassing the API | Silent invariant break, discovered days later | Import command routed through the model API + post-import verification |
| Abandoned dependency | Laravel major upgrade blocks | Adapter boundary + documented fallback package |
| Fork behaviour drift | Debugging against upstream docs misleads | Pin exact version, read the installed source when behaviour surprises |
Ramp-up
How can I help you explore Laravel packages today?