This package is not installable via Composer 1.x, please make sure you upgrade to Composer 2+. Read more about our Composer 1.x deprecation policy.

Recursive Laravel Eloquent relationships with CTEs

v2.1.2 2019-11-24 10:54 UTC

This package is auto-updated.

Last update: 2021-09-24 15:23:38 UTC


Build Status Code Coverage Scrutinizer Code Quality Latest Stable Version Total Downloads License


This Laravel Eloquent extension provides recursive relationships using common table expressions (CTE).

Supports Laravel 5.5.29+.


  • MySQL 8.0+
  • MariaDB 10.2+
  • PostgreSQL 9.4+
  • SQLite 3.8.3+
  • SQL Server 2008+


composer require paxha/laravel-adjacency-list


Getting Started

Consider the following table schema for hierarchical data:

Schema::create('users', function (Blueprint $table) {

Use the HasRecursiveRelationships trait in your model to work with recursive relationships:

class User extends Model
    use \Paxha\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

By default, the trait expects a parent key named parent_id. You can customize it by overriding getParentKeyName():

class User extends Model
    use \Paxha\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
    public function getParentKeyName()
        return 'parent_id';


The trait provides various relationships:

  • ancestors(): The model's recursive parents.
  • ancestorsAndSelf(): The model's recursive parents and itself.
  • children(): The model's direct children.
  • childrenAndSelf(): The model's direct children and itself.
  • descendants(): The model's recursive children.
  • descendantsAndSelf(): The model's recursive children and itself.
  • parent(): The model's direct parent.
  • parentAndSelf(): The model's direct parent and itself.
  • siblings(): The parent's other children.
  • siblingsAndSelf(): All the parent's children.
$ancestors = User::find($id)->ancestors;

$users = User::with('descendants')->get();

$users = User::whereHas('siblings', function ($query) {
    $query->where('name', '=', 'John');

$total = User::find($id)->descendants()->count();

User::find($id)->descendants()->update(['active' => false]);



The trait provides the tree() query scope to get all models, beginning at the root(s):

$tree = User::tree()->get();


The trait provides query scopes to filter models by their position in the tree:

  • hasChildren(): Models with children.
  • hasParent(): Models with a parent.
  • isLeaf(): Models without children.
  • isRoot(): Models without a parent.
$noLeaves = User::hasChildren()->get();

$noRoots = User::hasParent()->get();

$leaves = User::isLeaf()->get();

$roots = User::isRoot()->get();


The trait provides query scopes to order models breadth-first or depth-first:

  • breadthFirst(): Get siblings before children.
  • depthFirst(): Get children before siblings.
$tree = User::tree()->breadthFirst()->get();

$descendants = User::find($id)->descendants()->depthFirst()->get();


The results of ancestor, descendant and tree queries include an additional depth column.

It contains the model's depth relative to the query's parent. The depth is positive for descendants and negative for ancestors:

$descendantsAndSelf = User::find($id)->descendantsAndSelf()->depthFirst()->get();

echo $descendantsAndSelf[0]->depth; // 0
echo $descendantsAndSelf[1]->depth; // 1
echo $descendantsAndSelf[2]->depth; // 2

You can customize the column name by overriding getDepthName():

class User extends Model
    use \Paxha\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    public function getDepthName()
        return 'depth';

You can use the whereDepth() query scope to filter models by their relative depth:

$descendants = User::find($id)->descendants()->whereDepth(2)->get();

$descendants = User::find($id)->descendants()->whereDepth('<', 3)->get();


The results of ancestor, descendant and tree queries include an additional path column.

It contains the dot-separated path of primary keys from the query's parent to the model:

$descendantsAndSelf = User::find(1)->descendantsAndSelf()->depthFirst()->get();

echo $descendantsAndSelf[0]->path; // 1
echo $descendantsAndSelf[1]->path; // 1.2
echo $descendantsAndSelf[2]->path; // 1.2.3

You can customize the column name and the separator by overriding the respective methods:

class User extends Model
    use \Paxha\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;

    public function getPathName()
        return 'path';

    public function getPathSeparator()
        return '.';


Please see CONTRIBUTING and CODE OF CONDUCT for details.