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

Ltree Bundle Laravel Package

pvsaintpe/ltree-bundle

pvsaintpe/ltree-bundle is a Symfony bundle for integrating PostgreSQL ltree with Doctrine, enabling hierarchical tree data storage, path-based queries, and practical tree management for PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the reality check: this is a Symfony bundle (pvsaintpe/ltree-bundle), not a Laravel package. It wraps PostgreSQL's ltree extension into Doctrine DBAL types and helpers. In Laravel you'd be adapting the underlying concepts (custom DBAL type, migration, query builder) rather than using the bundle as-is. If you're on Symfony, install normally via Composer and register the bundle.

Minimum steps (Conceptual / Laravel adaptation):

  1. Enable the extension in Postgres — this is the true prerequisite:

    CREATE EXTENSION IF NOT EXISTS ltree;
    

    The bundle assumes ltree is already available; it does not install it.

  2. Add an ltree column to your table:

    ALTER TABLE categories ADD COLUMN path ltree;
    CREATE INDEX categories_path_gist ON categories USING GIST (path);
    

    The GiST index is what makes @> / <@ queries fast. A plain btree is nearly useless for ltree matching.

  3. Register the type so your ORM/framework treats ltree as a string, not as a blob/unknown:

    • Symfony + Doctrine: register the bundle (it supplies the LtreeType), or manually add the DBAL type.
    • Laravel/Eloquent: add a cast or raw select (->selectRaw('path::text')) since Eloquent has no native ltree support. Write the DBAL type yourself — it's ~20 lines (getName(): 'ltree', getSQLDeclaration(): 'ltree', and treat values as strings).
  4. Read the source first. With only 10 stars and a 2021 last release, the most useful "documentation" is the bundle's DependencyInjection/Configuration.php and the ltree SQL type class. Those tell you the exact column shape and helper signatures you're relying on.

First use case: hierarchical categories or org units. Store paths like root.electronics.phones, then:

-- all descendants of 'root.electronics'
SELECT * FROM categories WHERE path <@ 'root.electronics';
-- direct children only
SELECT * FROM categories WHERE path <@ 'root.electronics' AND nlevel(path) = 3;

Implementation Patterns

  • Path conventions. Build paths from stable, immutable segments (id or slug), joined with .. The path is the hierarchy — treat it like a materialized path, not a foreign key. Renaming a node's label means rewriting every descendant path.

  • Subtree move is the core operation. Do it in a single transaction:

    UPDATE categories
    SET path = new_prefix || subpath(path, nlevel(old_prefix))
    WHERE path <@ old_prefix;
    

    Always scope with <@ old_prefix and wrap in a transaction — a partial update corrupts the tree.

  • Query patterns to standardize in a repository:

    • Descendants: path <@ :anchor
    • Ancestors: path @> :node
    • Depth: nlevel(path)
    • Sorted depth-first traversal: ORDER BY path
    • Siblings at depth: path ~ 'root.category.*{1}'
  • Pair ltree with a plain FK/parent_id. ltree gives you fast subtree reads; keep parent_id for cheap single-row parent lookups and referential integrity. They serve different jobs.

  • Integrate at the repository layer, not the model. Whether Doctrine or Eloquent, hide ltree syntax behind methods like descendantsOf(), moveSubtree(), depthOf(). Raw ltree operators leak easily and are hard to test.

  • Migrations must own both the extension and the index. Add CREATE EXTENSION IF NOT EXISTS ltree as the first statement so fresh environments (CI, migrate:fresh) don't fail on a missing type.

Gotchas and Tips

  • This package is stale. Last release March 2021, 10 stars. Pin it explicitly, and verify it against your current Symfony/Doctrine major version before adopting. If anything breaks, you'll be maintaining a fork. The ltree SQL itself is stable across Postgres versions, so falling back to hand-written types + queries is often lower risk than depending on the bundle.

  • Label rules are strict. ltree labels are limited to alphanumerics, underscores, and (newer Postgres) hyphens. Dots are separators — you cannot include a literal dot in a label. Slugs with dots or spaces will silently mangle your tree. Sanitize segments before building paths.

  • Length limits. Each label is capped (255 bytes) and the whole path too; deep trees of long slugs hit this faster than expected.

  • lquery vs ltree vs ltext. The ~ operator expects an lquery; passing a plain ltree won't work. When constructing lquery patterns dynamically, quote/escape user input — lquery has metacharacters (*, @, !, |, {}) that invite injection or accidental wildcards.

  • Index choice matters. Without a GiST index, every <@/@> becomes a sequential scan. Confirm with EXPLAIN ANALYZE after adding data — small dev tables hide this.

  • Case sensitivity. ltree comparisons are case-sensitive. Decide on lowercase normalization at write time or you'll get split trees (root.Phones vs root.phones).

  • Moving subtrees is the #1 bug source. Forgetting the transaction, or matching with LIKE 'old.%' instead of an ltree operator, will catch sibling prefixes (root.cat matching root.category) and corrupt data. Use subpath() + <@, never string LIKE.

  • No ORM magic. The bundle provides a DBAL type; it does not give you an entity tree behavior, cascade, or events. All hierarchy maintenance (path recalculation on insert/move/delete) is yours. Centralize it in a service or model observer.

  • Debugging tip: when a query returns nothing unexpectedly, SELECT path::text, nlevel(path) FROM t to eyeball actual stored paths — invisible escaping/quoting problems are common.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console