Reputation: 1600
I have a table called terms
which contains the following fields:
id | name | slug | taxonomy | parent_id
This is the current datasource:
id | name | slug | taxonomy | parent_id
1 Pop pop category null
2 Rock rock category null
3 Jazz jazz category 2
4 Edm edm category 3
Essentially I want return as result something like this:
Pop
Rock
— Jazz
— — Edm
Because Jazz
and Edm
are childs of Rock
, so I wrote this:
return Term::where([
'taxonomy' => 'category'
])->orderBy('parent_id', 'ASC');
The problem's that when I order the Datatable by a specific column I doesn't get the desired order, and Eloquent
will return the records ordered by the column selected eg:
column: name
order: desc
result:
Rock
Pop
— Jazz
— — Edm
Expected result:
Rock
— Jazz
— — Edm
Pop
Term Model
<?php
namespace App\Cms\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Term extends Model
{
use HasFactory, Sluggable;
protected $table = 'terms';
protected $fillable = [
'name',
'slug',
'taxonomy',
'description',
'parent_id',
'term_group',
];
public function parent()
{
return $this->hasOne(Term::class, 'id', 'parent_id');
}
public function childs()
{
return $this->hasMany(Term::class, 'parent_id', 'id');
}
}
Recursion
function get_parent_term_name(Term $term, bool $root = true)
{
$name = "";
if (isset($term->parent)) {
$name .= "— " . get_parent_term_name($term->parent, false);
}
return $root
? $name . " " . $term->name
: $name;
}
Is there a way to achieve this?
Upvotes: 4
Views: 1692
Reputation: 1118
Your childs
relation needs to be recursive to achieve this, for this, you can first fetch all the parents i.e. the records with parent_id = null
since they will be the only records exiting as a parent but not child.
Term.php Model
public function parent()
{
return $this->hasOne(Term::class);
}
public function childs()
{
//Recursively call childs() relation treating each
//child as parent until no more childs remain
return $this->hasMany(Term::class, 'parent_id')->with('childs');
}
TermsController.php
return Term::where('parent_id',null)->with('childs')->orderBy('id', 'ASC');
This will give you a nested collection as:
0 => [
id: 1,
name: 'Pop,,
slug: 'pop',
taxonomy:'category',
parent_id:null,
childs: []
]
1 => [
id: 2,
name: 'Rock,,
slug: 'rock',
taxonomy:'category',
parent_id:null,
childs: [
id: 3,
name: 'Jazz,,
slug: 'jazz',
taxonomy:'category',
parent_id:2,
childs: [
id: 4,
name: 'Edm,,
slug: 'edm',
taxonomy:'category',
parent_id:3,
childs: []
]
]
]
Note:
Your code must have a termination condition because according to your requirements and this solution, the tree might end up in an infinite loop (provided your database contains billions of records)
Upvotes: 2