calvient/arbol
calvient/arbol is a Laravel package for managing hierarchical tree data in Eloquent models, with helpers for ancestors, descendants, nested sets, and ordered category structures. It simplifies building trees, querying relationships, and maintaining sorted node hierarchies.
Arbol is a Laravel package for building database-driven reports (columns, filters, sorts, groupings, aggregates, exports) without hand-rolling a query UI each time. The 1.0.4 release is a small bugfix, so the onboarding path is unchanged.
Minimal steps:
composer require calvient/arbol, then publish and run migrations (saved_reports and related tables store user-defined report state) and publish the config.config/arbol.php first — it controls route registration, middleware/auth guard for the report UI and saved-report endpoints, export drivers, and pagination defaults. Most "why is this 403 / 404" questions are answered here.Report). Start with the smallest possible definition: a base query, 3–4 columns, one filter, one sort. Resist adding aggregates until the simple report renders and exports.columns(), filters(), sorts(), groups(), and aggregates() far faster than the API surface.One report class per dataset, thin definitions. Keep column/filter declarations declarative and push business logic into Eloquent scopes or query builder macros. Reports become long and brittle when they contain inline DB::raw conditionals.
Sections are the unit of composition. A report can hold multiple sections, each with its own query and its own filters/sorts/columns. Treat a section as "one table/grid" — dashboards are usually several sections in one report rather than several report classes.
Saved reports are the persistence layer. Users define a report config (filters, sorts, visible columns, groupings) and you persist it. Build features against the saved-report object rather than re-deriving config from request input on every load, so saved state and live state stay consistent.
Per-section saved filters (1.0.4). Saved filters are now scoped to the report section that created them. This is the correct model — previously a filter saved against one section could bleed into another section of the same report. Design new saved-report features assuming filters belong to a section, and key any of your own caching/ETag logic on (saved_report_id, section).
Exports. Route anything non-trivial through the queue. Large exports against aggregate-heavy sections are the most common source of timeouts; keep a separate "export" code path that skips UI-only columns.
Testing. Assert on the report's declared structure (column keys, filter names, compiled query bindings) rather than snapshotting rendered HTML. A test that boots the report with a fixed input payload and asserts the resulting SQL/bindings catches most regressions cheaply.
ONLY_FULL_GROUP_BY enabled locally — this is the single most common "works on my machine" report failure.id/name/created_at will silently resolve to the wrong table once the report joins more than one.config/arbol.php is actually applied in your environment, and scope base queries by tenant explicitly. Don't assume the package does it for you.composer.json if you ship saved reports to end users; filter persistence semantics have now changed once in a patch release, so treat report-config serialization as a de facto public API.How can I help you explore Laravel packages today?