Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel ORM from self referencing table get N level hierarchy JSON

I am using LARAVEL 4 with MySQL back-end.

I have a self-referencing table with columns id, name, type and parent. Here, parent is foreign-key of the column Id. The data in table is as below :

id  name          type         parent 
1   General       group        NULL
2   What is..?    question     1
3   aa            answer       2
4   bb            answer       2
5   cc            answer       2
6   How is..?     question     1
7   ba            answer       6
8   bb            answer       6
9   Where is..?   question     4
10  ca            answer       9
11  cb            answer       9
12  Who is..?     question     6
13  da            answer       12
14  db            answer       12
15  Specific      group        NULL
16  When is..?    question     15
17  ea            answer       16
18  eb            answer       16
19  Whome is..?   question     2
20  fa            answer       19
21  fb            answer       19
22  fc            answer       19

I want a function that return nested JSON using this relational data. For example :

[{
  "id" : 1, 
  "name" : "Geneal", 
  "type" : "group", 
  "children" : [{
      "id" : 2, 
      "name" : "What is..?", 
      "type" : "question", 
      "children" : [{
         "id" : 3, 
         "name" : "aa", 
         "type" : "answer"
      },
      {
         "id" : 4, 
         "name" : "bb", 
         "type" : "answer"
      },
      {
         "id" : 5, 
         "name" : "cc", 
         "type" : "answer"
      }]},
      {
      "id" : 6, 
      "name" : "How is..?", 
      "type" : "question", 
      "children" : [{
         "id" : 7, 
         "name" : "ba", 
         "type" : "answer"
      },
      {
         "id" : 8, 
         "name" : "bb", 
         "type" : "answer"
      }]
   }]
... and so on
}]

I have created a model named Survey as below :

class Survey extends BaseModel{

    protected $table = 'questions';
    protected $softDelete = false;

    public function parent()
    {
        return $this->belongsTo('Survey', 'parent');
    }

    public function children()
    {
        return $this->hasMany('Survey', 'parent');
    }   
}

and called it in controller with :

$user = Survey::all();
$parent = $user->parent()->first();
$children = $user->children()->get();

But I am not getting the proper result as I have mentioned in JSON above.

print_r($parent->toJson()); 

gives records with one level hierarchy only (i.e group and questions, not answers).

while

print_r($children ->toJson());

Gives only questions (Not groups and answers).

I want the whole self-referencing data in nested JSON format with N level of hierarchy.

I also have tried

$user = Survey::with('parent', 'children')->get();

But found same as $parent above.

Is there anyway I can get the desired result?

Thanks in advance..

like image 229
Dev Avatar asked Jul 10 '14 09:07

Dev


People also ask

What is the Laravel eloquent Orm?

The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table.

What is Laravel response JSON?

Introduction to Laravel Response JSON. Laravel Response JSON, the Laravel framework has been in demand for the last few years. It has been able to garner a sizeable portion of the development framework market. The reasons are plenty. One of the most important features of the framework has been its expressive command line methods.

How do I create a JSON field in Laravel?

Back-end: Migration + Model To create a JSON field, all we need to do in Laravel migration is use ->json () method: Schema::create ('products', function (Blueprint $table) { $table->increments ('id'); $table->string ('name'); $table->decimal ('price', 15, 2); $table->json ('properties'); $table->timestamps (); $table->softDeletes (); });

What is the Laravel framework?

Much has been said about the Laravel framework library. This is a library that stores a vast amount of information. It is easy to access and as I had mentioned earlier expressive enough to provide multiple options for the same functionality. Let’s take for example the subject of this article: Laravel Response JSON.


2 Answers

Here's how you manually retrieve nested relations:

$collection = Model::with('relation1.relation2.relation3')->get();

So in your case it would be:

$surveys = Survey::with('children.children.children')->get();

Obviously this will do the job when the relations are fixed, but it's not the way to go for a recursive relation to the same table.

Fortunately, you can make such relation recursive, then all you need to retrieve whole tree is this:

$surveys = Survey::with('childrenRecursive');

However, I wouldn't load parent for each row this way.

So here's all you need:

// Survey model
// loads only direct children - 1 level
public function children()
{
   return $this->hasMany('Survey', 'parent');
}

// recursive, loads all descendants
public function childrenRecursive()
{
   return $this->children()->with('childrenRecursive');
   // which is equivalent to:
   // return $this->hasMany('Survey', 'parent')->with('childrenRecursive);
}

// parent
public function parent()
{
   return $this->belongsTo('Survey','parent');
}

// all ascendants
public function parentRecursive()
{
   return $this->parent()->with('parentRecursive');
}

EDIT: To get real tree structure, first query must be limited to only root nodes:

$surveys = Survey::with('childrenRecursive')->whereNull('parent')->get();
like image 88
Jarek Tkaczyk Avatar answered Sep 22 '22 11:09

Jarek Tkaczyk


so simple!!!

1- load your 'children' relation in your 'children' relation in your model like this :

class Survey extends BaseModel{

    protected $table = 'questions';
    protected $softDelete = false;

    public function parent()
    {
        return $this->belongsTo('Survey', 'parent');
    }

    public function children()
    {
       // change this: return $this->hasMany('Survey', 'parent');

        return $this->hasMany('Survey', 'parent')->with('children);
    }   
}

2- you just need add clouser function to your relation like this:

$surveys = Survey::with(['children' => function($q) {
            $q->with('children');
       },
       'parent' => function($q) {
            $q->with('parent');
       });

then you can access to your nested children in your result like this:

$surveys->children

and unlimited nesting :

$surveys->children->first()->children

and so on.

like image 23
اسماعیل زارع Avatar answered Sep 20 '22 11:09

اسماعیل زارع