29

I have a model CourseModule, and each of the items are related to the same model.

Database Structure:

enter image description here

Relation in Model:

    public function parent()
    {
        return $this->belongsTo('App\CourseModule','parent_id')->where('parent_id',0);
    }

    public function children()
    {
        return $this->hasMany('App\CourseModule','parent_id');
    }

I tried the following, but it returns only a single level of relation.

Tried:

CourseModule::with('children')->get();

I'm trying to create a json output like the following,

Expected Output:

[
  {
    "id": "1",
    "parent_id": "0",
    "course_id": "2",
    "name": "Parent",
    "description": "first parent",
    "order_id": "1",
    "created_at": "-0001-11-30 00:00:00",
    "updated_at": "-0001-11-30 00:00:00",
    "children": [
      {
        "id": "2",
        "parent_id": "1",
        "course_id": "2",
        "name": "Child 1",
        "description": "child of parent",
        "order_id": "2",
        "created_at": "-0001-11-30 00:00:00",
        "updated_at": "-0001-11-30 00:00:00",
        "children": [
          {
            "id": "3",
            "parent_id": "2",
            "course_id": "2",
            "name": "Child2",
            "description": "child of child1",
            "order_id": "2",
            "created_at": "-0001-11-30 00:00:00",
            "updated_at": "-0001-11-30 00:00:00",
            "children": [
              {
                "id": "4",
                "parent_id": "3",
                "course_id": "2",
                "name": "Child 3",
                "description": "child of child 2",
                "order_id": "2",
                "created_at": "-0001-11-30 00:00:00",
                "updated_at": "-0001-11-30 00:00:00",
                "children": []
              }
            ]
          }
        ]
      }
    ]
  }
]

I don't understand how to get the inner child objects.

Community
  • 1
  • 1
Kiran LM
  • 1,359
  • 1
  • 16
  • 29

6 Answers6

33

You should use with('children') in the children relation and with('parent') in the parent relations.

For your code to be recursive:

public function parent()
{
    return $this->belongsTo('App\CourseModule','parent_id')->where('parent_id',0)->with('parent');
}

public function children()
{
    return $this->hasMany('App\CourseModule','parent_id')->with('children');
}

Note: Make sure your code has some or the other exit conditions otherwise it will end up in a never ending loop.

Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Shyam Achuthan
  • 910
  • 5
  • 8
  • It works! In first collection I have a chained tree but in same children are repeated as parent collection. I don't want the children again as root collection. Is there any solution or am I doing wrong? – Shahbaz Ahmed Nov 22 '17 at 17:51
  • Found the solution `$data = CourseModule::with('children')->where('parent_id', 0)` – Shahbaz Ahmed Nov 22 '17 at 18:07
  • I am trying to display the results as the hierarchy, but could not. I tried something like this `@foreach ($accountHeads as $accountHead) @foreach ($accountHead->children as $children) @endforeach @endforeach` . But it showing only those, which has `parent_id` = `1` . – Rashed Hasan Jul 23 '18 at 20:24
10

You would have to recursively get the children if you have an unknown depth like that.

Another option is to use the nested sets model instead of the adjacency list model. You can use something like baum/baum package for Laravel for nested sets.

"A nested set is a smart way to implement an ordered tree that allows for fast, non-recursive queries." - https://github.com/etrepat/baum

With this package you have methods like getDescendants to get all children and nested children and toHierarchy to get a complete tree hierarchy.

Wikipedia - Nested Set Model

Baum - Nested Set pattern for Laravel's Eloquent ORM

Managing Hierarchical Data in MySQL

lagbox
  • 48,571
  • 8
  • 72
  • 83
  • Hi, `CourseModule::whereId($id)->first()->getDescendantsAndSelf()->toHierarchy()` returns only single node, should i have to make any changes to the model, i'm working with a single table as shown in top – Kiran LM Jan 13 '16 at 07:26
9

here is the answer that can help you

I think you you have to do it recursively to retrieve whole tree:

$data = CourseModule::with('child_rec');

Recursive function

This may help you according to your requirement,

public function child()
{
   return $this->hasMany('App\CourseModule', 'parent');
}
public function children_rec()
{
   return $this->child()->with('children_rec');
   // which is equivalent to:
   // return $this->hasMany('App\CourseModule', 'parent')->with('children_rec);
}
// parent
public function parent()
{
   return $this->belongsTo('App\CourseModule','parent');
}

// all ascendants
public function parent_rec()
{
   return $this->parent()->with('parent_rec');
}
Community
  • 1
  • 1
Sagar Naliyapara
  • 3,971
  • 5
  • 41
  • 61
4

Model Function:

public function Children()
{ 
    return $this->hasMany(self::class, 'Parent', 'Id')->with('Children');
} 

Controller Function:

Menu::with("Children")->where(["Parent" => 0])->get(); 
Ravikumar
  • 49
  • 1
4

your model relationship should like this

     // parent relation
     public function parent(){

        return $this->belongsTo(self::class , 'parent_id');
    }
    //child relation
     public function children()
    {
        return $this->hasMany(self::class ,'parent_id');
    }
    public function ascendings()
    {
        $ascendings = collect();

        $user = $this;

        while($user->parent) {
            $ascendings->push($user->parent);

            if ($user->parent) {
                $user = $user->parent;
            }
        }

        return $ascendings;
    }



    public function descendings()
    {
        $descendings = collect();
        $children = $this->children;

        while ($children->count()) {

            $child = $children->shift();

            $descendings->push($child);

            $children = $children->merge($child->children);

        }
        return $descendings;
    }
Mycodingproject
  • 1,031
  • 1
  • 9
  • 13
Abdulbasit
  • 41
  • 1
  • Please, can you extend your answer with more detailed explanation? This will be very useful for understanding. Thank you! – vezunchik Mar 24 '19 at 07:34
2

You can always create your own recursive function, in my case I do as the code as follow.

<?php
declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Akmeh\Uuid;

/**
 * Class Location
 * @package Domain\Models
 */
class Location extends Model
{
    use Uuid;

    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;
    public $timestamps = false;

    /**
     * @param string $id
     * @param array $tree
     * @return array
     */
    public static function getTree(string $id, array $tree = []): array
    {
        $lowestLevel = Location::where('id', $id)->first();

        if (!$lowestLevel) {
            return $tree;
        }

        $tree[] = $lowestLevel->toArray();

        if ($lowestLevel->parent_id !== 0) {
            $tree = Location::getTree($lowestLevel->parent_id, $tree);
        }

        return $tree;

    }

}
Pablo Morales
  • 687
  • 1
  • 5
  • 15