etrepat / baum

Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM.
http://etrepat.com/baum
MIT License
2.24k stars 459 forks source link

Get descendants of multiple nodes #289

Open alexdemers opened 7 years ago

alexdemers commented 7 years ago

I'm struggling everywhere to accomplish this. I need to have all the descendants of multiple nodes as: Category::whereIn('id', [<ids>])->getDescendantsAndSelf()

I'm aware that getDescendantsAndSelf() is part of the Node class and not Builder. I can achieved the results I want using the plain-old query builder, but I want to translate it to a collection of Node to be able to use toHierarchy():

$query = DB::table(with(new Category())->getTable().' a')
    ->join(with(new Category())->getTable().' b', function($join) {
        $join->on('b.Left', '>=', 'a.Left');
        $join->on('b.Right', '<=', 'a.Right');
    })
    ->whereIn('a.id', [<ids>]);

While I can translate the results to a Baum Collection using loops and declaring a new Collection and adding the items in it, I want to use special methods like with() before executing the get().

I tempted to extend the Node class to add this functionality, but before I do it, I want to know if there's something I'm missing to achieve this. Thanks.

alexdemers commented 7 years ago

So, I got it working by overriding newEloquentBuilder() of my Node class (Category). I know it's a hackish and not the best solution, but for now it solves my needs. If anyone else wants to accomplish that, here's the code:

Category.php

public function newEloquentBuilder($query) {
    return new Extensions\Baum\Builder($query);
}

Extensions\Baum\Builder.php

<?php
namespace Extensinos\Baum;
use DB;
class Builder extends \Illuminate\Database\Eloquent\Builder {
    public function descendantsAndSelf() {
        $builder = $this;
        $this->query->join(DB::raw($this->model->getTable().' descendants'), function($join) use ($builder) {
            $join->on('descendants.'.$builder->model->getLeftColumnName(), '>=', $builder->model->getQualifiedLeftColumnName());
            $join->on('descendants.'.$builder->model->getRightColumnName(), '<=', $builder->model->getQualifiedRightColumnName());
            $join->on('descendants.deleted_at', 'IS', DB::raw('NULL')); // quick hack because my class has soft delete traits
        })->select('descendants.*');
        return $this;
    }
}

Now you can use Category::whereIn('id', <ids>)->descendantsAndSelf()->get() and basically everything that the EloquentBuilder provides as methods.