I want to create dynamice mega menu but i'm getting error when i'm making tables relationship. Please let me know how can i make relationship.
Table structure Below:-
1) categories
id integer
category varchar
2) subcategories
id integer
subcategory varchar
category_id integer
3) parentcategories
id integer
parentname varchar
subcategory_id integer
Models
Categories.php
protected $table = "categories";
public function sub(){
return $this->hasManyThrough(Subcategories::class,Parentcategories::class,'subcategory_id','category_id');
}
Subcategories.php
protected $table = "subcategories";
public function category()
{
return $this->belongsTo('App\Categories');
}
Parentcategories.php
protected $table = "parentcategories";
public function subcategory()
{
return $this->belongsTo('App\Subcategories');
}
PagesController.php
public function index(){
$categories = Categories::all();
return view('pages.home')->with('categories',$categories);
}
blade template
#foreach($categories as $category)
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
{{ $category->category }} <i class="fa fa-chevron-down"
aria-hidden="true"></i>
</a>
<ul class="mega-menu dropdown-menu">
#foreach($category->sub->take(20) as $subcategory)
<li class="col-sm-3">
<h5>{{$subcategory->subcategory}} <i class="fa fa-caret-right" aria-hidden="true"></i></h5>
<ul class="mega-list">
<li>parent categories comes here</li>
</ul>
</li>
#endforeach
</ul>
</li>
#endforeach
Thanks
As I can see your table are hierarchy is in simillar fashion, I guess this can be done with single table. Create a table named categories and follow the below table structure:
Now create a model Category.php: something like this:
<?php
class Category extends Eloquent {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'categories';
public function parent() {
return $this->hasOne('category', 'id', 'parent_id');
}
public function children() {
return $this->hasMany('category', 'parent_id', 'id');
}
public static function tree() {
return static::with(implode('.', array_fill(0, 4, 'children')))->where('parent_id', '=', NULL)->get();
}
}
Now in your controller suppose HomeController.php write:
<?php
class HomeController extends BaseController {
protected $layout = "layouts.main";
public function showWelcome()
{
$items = Category::tree();
$this->layout->content = View::make('layouts.home.index')->withItems($items);
}
}
And in views suppose index.blade.php you can simply pass like this:
<ul>
#foreach($items as $item)
<li>{{ $item->title }}
#foreach($item['children'] as $child)
<li>{{ $child->title }}</li>
#endforeach
</li>
#endforeach
</ul>
This works for n-number for hierarchy, more efficient and a single table categories, hope this helps
Related
I'm Trying to display restaurant Menus grouped by the categories, for example...
Lunch
Chicken and Chips
Rice
Breakfast
Tea
Coffee
So I have 3 tables in my database, Restaurants, Categories, And Menus
Category Model
class Category extends Model
{
protected $fillable = [
'name'
];
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function menus_type()
{
return $this->hasMany('App\Menu','category_id');
}
}
Menu Model
class Menu extends Model
{
protected $fillable = [
'name',
'price',
'description',
'photoPath',
'restaurant_id',
'category_id',
];
/**
* Menu belongs to Restaurant
*/
public function restaurant()
{
return $this->belongsTo('App\Restaurant');
}
/**
* Menu belongs to category type
*/
public function category_type()
{
return $this->belongsTo('App\Category', 'category_id');
}
}
Restaurants controller
public function show($slug, RestaurantRepository $repository){
if(! $restaurant = $repository->get(['slug' => $slug], with(['cuisines','user', 'photos', 'thumbs', 'region', 'ratings.user']))) return abort('404');
$menus=Menu::with(['category_type'])->where('restaurant_id',$restaurant->id)->get()->groupBy('category_id');
return view('frontend.restaurant.show', compact('restaurant', 'p','menus'));
}
when i Dump this it looks just fine.
Results have been grouped
Now my trouble is on the View when i try to get results of this i get an error.
#if($menus)
<ul>
#foreach($menus as $m)
<li>
{{$m->name}}
</li>
#endforeach
</ul>
#endif
ErrorException (E_ERROR).
Property [name] does not exist on this collection instance.
Iterate inner loop also. 'groupBy()' creates another array with category_id as key.
#if($menus)
<ul>
#foreach($menus as $category_id=>$outer)
#foreach($outer as $k=>$inner)
<li>
{{$category_id}}: {{$inner->name}}
</li>
#endforeach
#endforeach
</ul>
#endif
Updated you query to get from category
$categories = Category::with('menus_type')->get();
return view('frontend.restaurant.show', compact('restaurant', 'p','categories '));
In your view
#if($categories ?? false)
<ul>
#foreach($categories ?? [] as $cat_key=>$category)
<li>
{{$category->name}}
</li>
<li>
<ul>
#foreach($category->menus_type as $menu_key=>$menu)
<li>
{{$menu->name}}
</li>
#endforeach
</ul>
</li>
#endforeach
</ul>
#endif
I have created nested treeview categories and subcategories but seems that it is rendering the categories twice.
This is what is happen and I see on page.
Main category 1
- Sub -category 1.1
- Sub -category 1.2
Main category 2
- Sub -category 2.1
- Sub -category 2.2
Sub -category 1.1
Sub -category 1.2
Sub -category 2.1
Sub -category 2.2
What is happening here is that first is render the correct treeview but then under is render sub-categories as main categories.
The categories and sub-categories are saved in one table where I have column parent_id 0 for main category and if is sub-category is the id of main-category instead. This is my model
public function parent()
{
return $this->belongsTo('App\Category');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
Controller
public function index()
{
$allCategories = Category::with(['children'])->get();
return view('categories', compact('allCategories'));
}
and the blade
#foreach($allCategories as $category)
<li class="treeview">
{{ $category->title }}
<ul class="treeview-menu">
#foreach($category->children as $subcategory)
<li class="">{{$subcategory->title}}</li>
#endforeach
</ul>
</li>
#endforeach
Try like this.
Model
public function parent()
{
return $this->belongsTo('App\Category', 'id', 'parent_id'); // I believe you can use also hasOne().
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id', 'id');
}
public static function tree() {
return static::where('parent_id', '=', NULL)->get(); // or based on you question 0?
}
Your controller
public function index()
{
$allCategories = Category::tree();
return view('categories', compact('allCategories'));
}
And your blade view
#foreach($allCategories as $item)
<li class="treeview">
<i class="fa fa-link"></i> <span>{{ $item->title }}</span> <i class="fa fa-angle-left pull-right"></i>
<ul class="treeview-menu">
#foreach($item['children'] as $child)
<li>{{ $child->title }}</li>
#endforeach
</ul>
</li>
#endforeach
I have simple query which populate menu with Main categories and sub-categories. This work perfectly
// in controller
$cat=Categories::all();
View::share('categories_menu',$cat);
// in view
#foreach($categories_menu as $category_menu)
#if($category_menu->has('subcategories'))
<ul class="nav nav-stacked cat-nav">
<li>
{{ $category_menu['category_name'] }}
#if($category_menu->subcategories->count())
<ul>
#foreach($category_menu->subcategories as $subcategory)
<li class="">{{$subcategory->sub_cat_name}}</li>
#endforeach
</ul>
#endif
</li>
#else
<li><i class="fa fa-link"></i> <span>{{ $category_menu->category_name }}</span></li>
#endif
</ul>
#endforeach
This make output like
Main Category
-Sub Cat
-Sub Cat
Second main category
-Sub in second main category
...
The problem is that it is loading also sub-categories which are empty. I don't want to show them and I tried to change the query with joining all three tables - category, sub_categories, products
This is the query so far
$cat = Categories::select('*', DB::raw('category.category_id AS category_id'))
->leftJoin('products', function($join) {
$join->on('products.category_id', '=', 'category.category_id');
})
->leftJoin('sub_category', function($secondjoin){
$secondjoin->on('sub_category.category_id', '=', 'category.category_id');
})
->whereNotNull('products.sub_cat_id')
->get();
The result is strange ( image )
It's should show me only two main categories each with one sub. On the image you can see my product table
Category 1
-sub1
Category 2
-sub3
If need some more source or something i can provide.
Update with models:
Categories model
class Categories extends Eloquent {
protected $table = 'category';
protected $primaryKey = 'category_id';
public $timestamps = false;
public function products()
{
return $this->hasMany('Product', 'category_id');
}
public function subcategories()
{
return $this->hasMany('SubCategories', 'category_id');
}
}
SubCategories model
class SubCategories extends Eloquent {
protected $table = 'sub_category';
protected $primaryKey = 'sub_cat_id';
public $timestamps = false;
public function category()
{
return $this->belongsTo('Category', 'category_id');
}
public function products()
{
return $this->hasMany('Product', 'sub_cat_id');
}
}
Products model
class Product extends Eloquent {
protected $table = 'products';
protected $primaryKey = 'product_id';
public function categories()
{
return $this->hasMany('Categories', 'category_id');
}
public $timestamps = false;
}
#foreach($categories_menu as $category_menu)
#if($category_menu->has('subcategories'))
<ul class="nav nav-stacked cat-nav">
<li>
{{ $category_menu['category_name'] }}
#if($category_menu->subcategories->count())
<ul>
#foreach($category_menu->subcategories as $subcategory)
#if($subcategory->products->count())
<li class="">{{$subcategory->sub_cat_name}}</li>
#endif
#endforeach
</ul>
#endif
</li>
#else
<li><i class="fa fa-link"></i> <span>{{ $category_menu->category_name }}</span></li>
#endif
</ul>
#endforeach
I added a condition for subcatergy to be displayed only if their products count is above 0.
I have two models called category and sub-category
Category.php
class Category extends Model
{
protected $table = 'category';
public $timestamps = false;
public function subCategory(){
return $this->hasMany('App\Subcategory', 'category_id');
}
}
Subcategory.php
class Subcategory extends Model
{
protected $table = 'subcategory';
public $timestamps = false;
public function subCategory() {
return $this->belongsTo('App\Category');
}
and I have foreign key column called category_id in my subcategory table in database
this is how I am trying to get all the subcategories for the selected category in my controller
$subcategory = Category::all();
and my blade view
<ul>
#foreach($categories as $categories)
<li class='has-sub'><a href='#'>{{ $categories->category_name }}</a>
<ul>
#foreach($subcategory->subCategory() as $sub)
<li><a href='#'>{{ $sub->subcategory_name }}</a></li>
#endforeach
</ul>
</li>
#endforeach
</ul>
I am able to get all the categories name for now but I can't get the name of sub-categories for the category..What I am missing here?
Your models are fine, i would modify the controller and view to the following:
In your controller
$categories = Category::with('subCategory')->get();
Now in this this case, your categories will be eager loaded with your subcategories. In your example, you make queries in a foreach loop, which is not efficient.
In your view
<ul>
#foreach($categories as $category)
<li class='has-sub'><a href='#'>{{ $category->category_name }}</a>
<ul>
#foreach($category->subCategory as $sub)
<li><a href='#'>{{ $sub->subcategory_name }}</a></li>
#endforeach
</ul>
</li>
#endforeach
</ul>
Notice the variable names, as #aldrin27 mentioned in the comment! ($categories -> $category)
More tweaks
You could use one model for categories and subcategories:
function subCategory() {
return $this->hasMany('App\Category', 'category_id');
}
So i have in my database a table with id, id_parent, title and so on..
I need to create a list with sublists from that. I need some sort of recursive function but don't know how to do that in laravel..
I tried
class Goals extends Model {
protected $table = 'goals';
public function subgoals() {
return $this->hasMany(SubGoals::class, 'id_category');
}
}
class SubGoals extends Model {
protected $table = 'goals';
public function goals() {
return $this->belongsTo(Goals::class, 'id_category');
}
}
Controller:
$treeView = Goals::with(['SubGoals'])->get();
And view:
#foreach($treeView as $category)
<li>
{{ $category->title }}
<ul>
#foreach($category->subgoals as $subcategory)
<li>{{ $subcategory->title }}</li>
#endforeach
</ul>
</li>
#endforeach
Didn't get the right result..
Maybe someone have a snippet..
You dont need to create 2 classes, just need one like your example Goal.
class Goal extends Model {
protected $table = 'goals';
public function subgoals() {
return $this->hasMany(Goal::class, 'parent_id', 'id');
}
}
then in the controller you need to "Query all parent Goals" like this:
$parent_goals = Goal::whereNull('parent_id')->get();
and finally in the view:
#foreach($parent_goals as $goal)
<li>
{{ $goal->title }}
<ul>
#foreach($goal->subgoals as $subgoal)
<li>{{ $subgoal->title }}</li>
#endforeach
</ul>
</li>
#endforeach
that's it. hope that help's you.