Laravel query builder for recursive results? E.g. id, parent_id

前端 未结 5 1904
生来不讨喜
生来不讨喜 2021-02-06 07:39

So I have data structured like this:

id|parent_id|name
1 |null     |foo
2 |1        |bar
3 |2        |baz

So basically foo->bar->ba

相关标签:
5条回答
  • 2021-02-06 08:13

    I've created a package that uses common table expressions (CTE) to implement recursive relationships: https://github.com/staudenmeir/laravel-adjacency-list

    You can use the ancestors relationship to get all parents of a model recursively:

    class YourModel extends Model
    {
        use \Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;
    }
    
    $allParents = YourModel::find($id)->ancestors;
    
    0 讨论(0)
  • 2021-02-06 08:18

    An other way could be to use the etrepat/baum package, it's a Laravel implementation of the Nested set model. It's using an ordered tree that is faster and use non-recursive queries. While your data structured like this :

    root
      |_ Child 1
        |_ Child 1.1
        |_ Child 1.2
      |_ Child 2
        |_ Child 2.1
        |_ Child 2.2
    

    There are structured like this in nested set model :

     ___________________________________________________________________
    |  Root                                                             |
    |    ____________________________    ____________________________   |
    |   |  Child 1                  |   |  Child 2                  |   |
    |   |   __________   _________  |   |   __________   _________  |   |
    |   |  |  C 1.1  |  |  C 1.2 |  |   |  |  C 2.1  |  |  C 2.2 |  |   |
    1   2  3_________4  5________6  7   8  9_________10 11_______12 13  14
    |   |___________________________|   |___________________________|   |
    |___________________________________________________________________|
    

    And inserting nodes is easy as :

    $child1 = $root->children()->create(['name' => 'Child 1']);
    
    0 讨论(0)
  • 2021-02-06 08:22

    I modified tiffanyhwang solution and turned it into a non-static method and included a attribute accessor to make it easier to get results.

    public function ancestors()
    {
        $ancestors = $this->where('id', '=', $this->parent_id)->get();
    
        while ($ancestors->last() && $ancestors->last()->parent_id !== null)
        {
            $parent = $this->where('id', '=', $ancestors->last()->parent_id)->get();
            $ancestors = $ancestors->merge($parent);
        }
    
        return $ancestors;
    }
    

    and accessor to retrieve a collection of ancestors from model attribute

    public function getAncestorsAttribute()
    {
        return $this->ancestors();
        // or like this, if you want it the other way around
        // return $this->ancestors()->reverse();
    }
    

    so now you can get ancestors like this:

    $ancestors = $model->ancestors;
    

    and since its a Collection, you can now easily do for example this:

    echo $model->ancestors->implode('title',', ');
    
    0 讨论(0)
  • 2021-02-06 08:34

    So after fiddling around with the merge() method for the Collections class:

    public static function ancestors($id)
    {
        $ancestors = Model::where('id', '=', $id)->get();
    
        while ($ancestors->last()->parent_id !== null)
        {
          $parent = Model::where('id', '=', $ancestors->last()->parent_id)->get();
          $ancestors = $ancestors->merge($parent);
        }
    
        return $ancestors;
    }
    

    That will produce what I needed, however I believe it can be more cleaner so please feel free to edit it!

    0 讨论(0)
  • 2021-02-06 08:39

    I modified ruuter answer to use relationships. If you have a parent() belongsTo relationship on the model you can use that one to remove the where clause, see below:

    public function parents()
    {
            $parents = $this->parent()->get();
    
            while ($parents->last() && $parents->last()->parent_id !== null) {
                    $parent = $parents->last()->parent()->get();
                    $parents = $parents->merge($parent);
            }
    
            return $parents;
    }
    

    And then you can access it:

    public function allParents(): Collection 
    {
            return $this->parents();
    }
    
    0 讨论(0)
提交回复
热议问题