You could use this package for it.
Categories Tree View?
so i wanna display my categories as a tree view.
My eloquent logic is that the category model has category_id property and the relationship:
public function subcategory() {
$this->belongsTo('App\Category');
}
so its in relationship with it self, essentially forever alone :D
So I am satisfied with the logic now how would I represent this with li in my view? any ideas?
interesting.. Maybe something like this?
// Controller
$categories = App\Category::where('category_id', null) ->get();
// View
<ul>
@foreach($categories as $category)
<li>{{ $category->name }}
@if(count( $category->subcategory) > 0 )
<ul>
@foreach($category->subcategory as $subcategory)
<li>{{ $subcategory->name }}</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
Really interesting theory there :P
@askaoru Ill try your code and let you know if it works. From first glance it looks very promising. Thanks.
You could have a query scope to return only the "root" categories (category where category_id = 0 or null).
public function scopeRoot($query)
{
return $query->where('category_id', 0); //or null, nut sure how you are handling
}
Then on your controller
$root_elements = Category::with("subcategory")->root()->get();
//pass this variable to your view
and on your view
@foreach($root_elements as menu)
<li>{{menu.name}}
<ul>
@foreach(menu.subcategory as submenu)
<li> {{submenu.name}}</li>
@endforeach
</ul>
@endforeach
You could keep nesting foreachs if you have more then 2 levels. And you could validate if a menu has submenus before adding the UL, but overall that's how I would approach it.
@askaoru was faster, nice :D
I'm using a helper function to generate and display category tree view in blade files.
// app/Http/helpers.php
if (! function_exists('generateCategoryLists')) {
function generateCategoryLists(array $elements, $parentId = 0,$indent = 0) {
foreach ($elements as $key => $element) {
if ($element['parent_id'] == $parentId) {
echo '<li>' . $element['category_name'] . '</li>;
$children = generateCategoryLists($elements, $element['id'],$indent + 1);
}
}
}
}
// controller
$categories = Category::select('id', 'parent_id', 'category_name')->get()->toArray();
// create.balde.php
<ul>
{!! generateCategoryLists($categories, $parentId=0, $indent=0) !!}
</ul>
Category Table Structure
id, parent_id, category_name
@askaoru With the first code you suggested I get an error:
ErrorException in Model.php line 2717:
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation (View: C:\wamp\www\azurtours\resources\views\admin\Offers\add.blade.php)
any ideas? I like your code the most because it has the least lines :) but I will try also @igorblumberg and @kyawzinwin suggestions
@kyawzinwin I implemented your snippets but i get only the list without indentation?
@igorblumberg The scope root function do I implement it within my category class?
Does your BelongsTo specify which column points to the parent category? For example I have a similar situation where I have component "classes". Each component class potentially has a parent component class. My belongsTo relationship in my ComponentClass model looks like this ...
public function ParentClass() { return $this->belongsTo('ComponentClass', 'parent_class_id'); }
maybe thats something, i mean I have the category_id column so i thought if i do belongsTo it would assume that the parent column is category_id.
I have only one class not 2, and this:
class Category
...
public function subcategory() {
$this->belongsTo('App\Category');
}
is referencing itself
I was dealing with the same few weeks ago. What I actually used were two packages:
laravel-nestedset which nicely handles categories, they are easy to add and modify. You can also get children, descendants, and so on, this one is great for backend side of this.
Then for displaying the menu (ul) and getting currently active category, I used this package:
I wrote an 'adapter' so the laravel-menu can get correct data from the database and create the actual menu based on category tree and active category. Laravel menu itself handles active category based on the route used and adds 'active' class to the specific 'li' If you want I could provide the 'adapter', however its pretty messy right now :)
@kickthemooon You are forgetting the return statement.
public function subcategory() {
return $this->belongsTo('App\Category');
}
@kickthemooon i tested everything out. And instead of making it belongsTo, changing it hasMany works better.
And as @thomaskim said above, you forgot to 'return' the relationship which gave you that first error
These were what I did.
// Category.php , the model
public function subcategory()
{
return $this->hasMany(Category::class);
}
// The route (you can put this on the controller)
Route::get('category', function() {
$categories = \App\Category::where('category_id', null)->get();
return view('category', compact('categories'));
});
// The view (same code as above)
<ul>
@foreach($categories as $category)
<li>{{ $category->name }}
@if(count( $category->subcategory) > 0 )
<ul>
@foreach($category->subcategory as $subcategory)
<li>{{ $subcategory->name }}</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
The database

And the result

Extra: as @igorblumberg 's example, you might want to eager load this to reduce the number of your queries incase you have a lot categories/subcategories.
$categories = \App\Category::where('category_id', null)->with('subcategory')->get();
Hi @kickthemooon,
I think I forgot to add <ul> inside the recursive function. Can you try with the following edited function?
// app/Http/helpers.php
if (! function_exists('generateCategoryLists')) {
function generateCategoryLists(array $elements, $parentId = 0,$indent = 0) {
foreach ($elements as $key => $element) {
if ($element['parent_id'] == $parentId) {
echo '<ul>';
echo '<li>' . $element['category_name'] . '</li>;
echo '</ul>';
$children = generateCategoryLists($elements, $element['id'],$indent + 1);
}
}
}
}
In addition to @askaoru 's note - If you have more than one level of nesting you should extract partial and you it recursively
category.partial.blade.php:
<ul>
@foreach($categories as $category)
<li>{{ $category->name }}
@if(count( $category->subcategory) > 0 )
@include('category.partial', ['categories' => $category->subcategory])
@endif
</li>
@endforeach
</ul>
@kickthemooon yes, query scopes should go on your model:
Please or to participate in this conversation.