Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel get all parents of category

Tags:

php

laravel

I have categories and subcategories in laravel

namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $table = 'categories';

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

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

And I can add a new category which belongs to an already created category. Something like "Test -> subcategory of test" and then let's say I will create "sub-subcategory of test" which belongs to subcategory of test which will look like this: "Test -> subcategory of test -> sub-subcategory of test" And in the view I want to print the parents of the category if it has any. I do it like this:

@if($category->parent)
   <td>{{ $category->parent->name }} <strong>-></strong> {{ $category->name }}</td>
@else
   <td>{{ $category->name }}</td>    
@endif

But this only shows one parent category ("subcategory of test -> sub-subcategori of test"). I want to show all the parents which will look like this: "Test->subcategory of test -> sub-subcategory of test" How can I do that?

like image 275
Angel Miladinov Avatar asked Sep 01 '17 09:09

Angel Miladinov


3 Answers

I know this is old but here is the correct answer for anybody in the future. This will get you unlimited categories

In Category Model

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

public function getParentsNames() {

    $parents = collect([]);

    if($this->parent) { 
        $parent = $this->parent;
        while(!is_null($parent)) {
            $parents->push($parent);
            $parent = $parent->parent;
        }
        return $parents;
    } else {
        return $this->name;
    }

}

In Blade (I am sure you can do this on the model level also but here is my fast way of doing it)

 @if ($category->getParentsNames() !== $category->name)
                                @foreach ($category->getParentsNames()->reverse() as $item)
                                    @if ($item->parent_id == 0)
                                        <li class="breadcrumb-item"><a href="/{{ $item->slug }}">{{ $item->name }}</a></li>
                                             @else
                                        <li class="breadcrumb-item"><a href="../{{ $item->slug }}">{{ $item->name }}</a></li>
                                    @endif
                                @endforeach  
                            @endif

Result Home / Living Room / Sofa / Couch

like image 123
xavx Avatar answered Nov 11 '22 20:11

xavx


You could define a model accessor on Cateory model that retrieves all parents and puts them into a collection. So in your Category model add the following method:

public function getParentsAttribute()
{
    $parents = collect([]);

    $parent = $this->parent;

    while(!is_null($parent)) {
        $parents->push($parent);
        $parent = $parent->parent;
    }

    return $parents;
}

then in your blade template you can fetch all the parents of a category

@if(count($category->parents))
    <td>{{ $category->parents->implode('-') }} <strong>-></strong> {{ $category->name }}</td>
@else
    <td>{{ $category->name }}</td>
@endif

I see no other solution different from recursively retrieving all the parents and the package suggested by @Ross Wilson in the comments. Take into account that the number of queries will be equal to the number of the parent categories.

Another thing that you could do is setting the parent tree into the DB in the categories table, to save a lot of queries. Add in your categories table a string field like parents_tree and at save/update time set this value with the parent names already concatenated. This is a little dirty because if one of the parents change its name you have to update all the children accordingly. Is up to you to which trade-off you want to succumb.

like image 29
Desh901 Avatar answered Nov 11 '22 19:11

Desh901


you can append the parent variable:

class Category extends Model
{
    protected $table = 'categories';

    protected $appends = [
        'parent'
    ];

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

It's the shortest solution, but this is not the best way, since then every time a parent object is loaded. Better do this:

class Category extends Model { protected $table = 'categories';

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

public function getParentsNames() {
    if($this->parent) {
        return $this->parent->getParentsNames(). " > " . $this->name;
    } else {
        return $this->name;
    }
}

In your blade you call: {{ $category->getParentsNames() }}

like image 4
cre8 Avatar answered Nov 11 '22 20:11

cre8