Weave Code
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

Laravel Adjacency List Laravel Package

staudenmeir/laravel-adjacency-list

Eloquent extension that adds recursive relationships for trees (one parent) and graphs (many parents) using SQL common table expressions. Traverse ancestors/descendants and other hierarchies across MySQL, MariaDB, Postgres, SQLite, and SQL Server.

View on GitHub
Deep Wiki
Context7

Getting Started

Start by installing the package via Composer and adding the Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships trait to your Eloquent model (e.g., Category, MenuItem, OrganisationalUnit). The model must have a parent_id column (nullable for root nodes).

composer require staudenmeir/laravel-adjacency-list

Define a basic model:

use Illuminate\Database\Eloquent\Model;
use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

class Category extends Model
{
    use HasRecursiveRelationships;

    protected $fillable = ['name', 'parent_id'];
}

Run the initial migration to create the table:

Schema::create('categories', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->unsignedBigInteger('parent_id')->nullable()->index();
    $table->timestamps();
});

Your first use case: retrieve all descendants of a category with filtering:

$root = Category::find(1);
$nested = $root->descendants()->where('active', true)->get(); // Depth-first, eager-loaded

First thing to read: the README’s “Usage” section covers core methods like ancestors(), children(), descendantsAndSelf(), and path().

Note: This package now requires Laravel 10+ (Laravel 13 fully supported as of v1.26). Firebird is no longer supported; ensure your application targets MySQL, PostgreSQL, SQL Server, or SQLite ≥ 3.8.4.


Implementation Patterns

Recursive relationships via Eloquent

  • Use built-in relationships for intuitive access:
    $category->parent;               // Single parent
    $category->children;             // Direct children (eager-loadable)
    $category->ancestors;            // All ancestors, root-to-leaf order
    $category->descendants;          // All descendants, depth-first
    $category->descendants()->where('status', 'published')->get(); // Scoped
    $category->ancestorsAndSelf(); $category->descendantsAndSelf();
    $category->path(); // Returns path from root to current node as a collection
    

Query constraints and ordering

  • Use inNaturalOrder() to sort descendants by id within sibling groups, or orderByDepth() to sort by hierarchy level:
    $root->descendants()->orderByDepth()->get();
    $root->children()->inNaturalOrder()->get();
    

Nest structures efficiently

  • Combine descendants() with groupBy('parent_id') or use toTree() to convert results into nested arrays:
    $tree = Category::query()->toTree(); // Returns fully nested tree
    // Or for filtered trees:
    $tree = Category::where('active', true)->toTree();
    

Conditional recursive scopes

  • Apply reusable constraints to recursive parts with whereRecursive():
    Category::where('slug', 'electronics')
            ->descendants()
            ->whereRecursive(function ($query) {
                $query->where('visible', true);
            })
            ->get();
    

Cross-database portability

  • The package handles SQL dialects automatically (CTEs are supported in modern MySQL, PostgreSQL, SQL Server, and SQLite ≥ 3.8.4). No DB-specific query changes needed.

Gotchas and Tips

Database compatibility
Support for Firebird has been dropped in v1.26. If you rely on Firebird, pin to v1.25.x. For production, prefer PostgreSQL or MySQL 8.0+.

Laravel 13 compatibility
This release adds explicit support for Laravel 13. Ensure your composer.json allows laravel/framework ^13.0. No breaking API changes were introduced, but the minimum supported Laravel version is now 10 (previous versions <10 are unsupported).

Eager loading with nested constraints
Eager-loading relationships (e.g., Category::with('descendants')) applies to all descendants, but whereRecursive() won’t apply to the top-level model — only to the recursive part. To filter the whole tree, combine where() and whereRecursive().

Performance Gotcha: Depth Limits
By default, recursive queries are unbounded. For deep trees (e.g., >100 levels), set a max depth manually using where('depth', '<=', 10) after querying descendants (note: depth is not stored — it's computed per-query). Use orderByDepth() to group children by level.

Debugging recursive queries
Enable query logging (DB::enableQueryLog()) and inspect the with clause generated — it reveals how the package constructs CTEs. Look for laravel_recursive table aliases in the raw SQL.

Customizing relationships
If your parent_id column is named differently, override the getParentIdColumn() method:

public function getParentIdColumn()
{
    return 'branch_id';
}

Scoping by path prefix
Need to scope descendants under a specific slug or path segment? Use whereRaw() with recursive column references:

$parentPath = Category::find(5)->path()->pluck('slug')->join('/');
Category::descendants()
        ->whereRaw("CONCAT('/', GROUP_CONCAT(slug) SEPARATOR '/') LIKE ?", ["%/$parentPath/%"])
        ->get();

… though usually ancestors()->where('slug', ...)->exists() suffices.

Avoid N+1 in trees
Use with('children') for shallow trees or with('descendants') for deep ones — but be mindful of result set size. For menus/org charts, prefer toTree() after fetching root nodes and applying loadMissing('children') where needed.

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport