Laravel iterate through unlimited hierarchy treeview non-recursively - php

I have implemented printing a categories tree to the page recursively:
foreach ($categories as $category) {
$tree .='<li>'.$category->title.'';
if(count($category->childs)) {
$tree .=$this->childView($category);
}
}
$tree .='<ul>';
return view('categoryTreeview',compact('categories','allCategories','tree'));
}
public function childView($category){
$html ='<ul>';
foreach ($category->childs as $arr) {
if(count($arr->childs)){
$html .='<li>'.$arr->title.'';
$html.= $this->childView($arr);
}else{
$html .='<li>'.$arr->title.'';
$html .="</li>";
}
}
$html .="</ul>";
return $html;
}
For DB structure: id|title|parent_id
Now I need to implement a iterative way to print a category tree to the page, and so far I have not found a solution.
I also tried:
function buildTree($categories) {
$childs = array();
foreach($categories as $category)
$childs[$category->parent_id][] = $category;
foreach($categories as $category) if (isset($childs[$category->id]))
$category->childs = $childs[$category->id];
return $childs[0];
}
$treez = buildTree($categories);
But I also don't know how to use this data non-recursively.
Could anyone lead me on the right path? Maybe I should combine foreach loops with some kind of while condition?

In your category model:
public function subCategories()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
public function children()
{
return $this->subCategories()->with('children');
}
In your model:
public function getCategories()
{
$categories = Category::whereNull('parent_id')->with('children')->get();
return view('category', ['categories' => $categories]);
}
Blade (category.blade.php)
<div class="tree">
#include('category-list-widget',['categories' => $categories])
</div>
Blade (category-list-widget.blade.php)
<ul>
#foreach($categories as $category)
<li>
<a>{{$category->name}}</a>
#if(!empty($category->children) && $category->children->count())
#include('category-list-widget',['categories' => $category->children])
#endif
</li>
#endforeach
</ul>
I did not test the code, Just wrote it here directly in the texteditor. I hope you get the idea.

Related

passing multiple variable to one view in laravel 5.6

hello to all I want to pass multiple variables to one view
this is my CategoryController.php
public function site()
{
$categories = Category::all();
return view('template.sitemap', ['categories' => $categories]);
}
and this is SubCategoryController.php
public function index2(){
$subcategories = SubCategory::all();
return view('template.sitemap',['subcategories'=>$subcategories]);
}
this is my route for this action in web.php
Route::get('sitemap.html','CategoryController#site')->name('sitemap')
Route::get('sitemap.html','SubCategoryController#index2')->name('sitemap');
and this is the view i am trying to do this sitemap.blade.php
#foreach($categories as $category)
<li>{{$category->name}}</li>
<ul>
#foreach($subcategories as $subcategory)
<li><a href="category.html">{{$subcategory->category_name->name}</li>
#endforeach
</ul>
#endforeach
but i constantly see undefind vairalble
alone they work good
but when i want user both variables seee undefined vairable.
Your site will go to the first route and will never go to your second controller.
You should rather write.
Route
Route::get('sitemap.html','CategoryController#site')->name('sitemap');
Controller
public function site(){
$data = array();
$data['subcategories'] = SubCategory::all();
$data['categories'] = Category::all();
return view('template.sitemap',compact("data"));
}
View
#foreach($data['categories'] as $category)
<li>{{$category->name}}</li>
<ul>
#foreach($data['subcategories'] as $subcategory)
<li><a href="category.html">{{$subcategory->category_name->name}}</li>
#endforeach
</ul>
#endforeach
you can write
public function site()
{
$categories = Category::all();
$subcategories = SubCategory::all();
return view('template.sitemap', compact('categories', 'subcategories');
}
or you can eager load this
public function site()
{
$categories = Category::with('subcategories')->get();
return view('template.sitemap', compact('categories');
}
in view
#foreach($categories as $category)
<li>{{$category->name}}</li>
<ul>
#foreach($category->subcategories as $subcategory)
<li><a href="category.html">{{$subcategory->name}}</li>
#endforeach
</ul>
#endforeach

Iterating through collection - laravel

I am developing an online ecommerce for the first time and currently, i am not able to iterate through my collection.
Every item for a shop is categorized into a product category. My relationship is below
Category
public function items()
{
return $this->belongsToMany('App\Item','category_item','category_id','item_id')
->withTimestamps();
}
Items
public function categories()
{
return $this->belongsToMany('App\Category','category_item','item_id','category_id')
->withTimestamps();
}
This code here is able to fetch the groups and their products. I try to loop through to get the names of the products like below but it only displays the name of the last product in the database.
Why is this happening?
ItemController
//get id of product categories and display all products in grid table
$items_in_table = Category::whereIn('id',$request->get('product_category'))->with('products')->get();
foreach($items_in_table as $item)
{
return $item->name;
}
update
$temp
foreach($items_in_table as $item)
{
temp = $item;
}
return $temp
response
{"id":2,"title":"freight","no_of_contacts":0,"user_id":1,"created_at":"2018-04-15 23:55:30","updated_at":"2018-04-15 23:55:30","items":[{"id":1,"name":"AirBag ","phone":"0247878234","group_id":null,"user_id":1,"created_at":"2018-04-16 00:14:20","updated_at":"2018-04-16 05:31:05","pivot":{"group_id":2,"customer_id":1,"created_at":"2018-04-16 05:33:08","updated_at":"2018-04-16 05:33:08"}}
enter image description here
let's define our relationships first
Category:
public function items()
{
return $this->belongsToMany('App\Item')
->withTimestamps();
}
Item:
public function categories()
{
return $this->belongsToMany('App\Category')
->withTimestamps();
}
then in your controller:
$items_in_table = Category::with('items')->get();
$names = [];
foreach($items_in_table as $category) {
foreach($category->items as $item) {
$names[] = $item->name;
}
}
dd($names);
Maybe you need to put it in an array and then rotate it
$items_in_table = Category::whereIn('id',$request->get('product_category'))->with('products')->get();
$name = [];
foreach($items_in_table as $item)
{
$name[] = $item->name;
}
return $name;
foreach will not manipulate the values of the array
Some possible solutions:
Use a reference, this will change the values in $items_in_table
foreach($items_in_table as &$item)
{
$item = $item->name;
}
Use map (since it is an Laravel-collection), this will also alter the array
$items_in_table->map(function ($item) {
return $item->name;
})
you can Access any relation like this way .
For View what is you are getting. you can also use dd($item->quoteReturnDetail)
return collect($models->items())->transform(function ($item, $index){
if( $item->quoteReturnDetail){ dd($item->quoteReturnDetail); } });

Item count isn't showed on parent category

I'm trying to show items count on parent and on child category. So far the count is visible only on child category but when the item is assigned only to parent directory is showing 0. I have added this in my Category model
public function addRelation( $categoires )
{
$categoires->map(function( $item, $key)
{
$sub = $this->selectChild($item->id);
$item->itemCount = $this->getItemCount($item->id , $item->parent_id );
return $item = array_add($item, 'subCategory', $sub);
});
return $categoires;
}
public function getItemCount( $category_id, $parent_id )
{
if( $parent_id == 0)
{ // for root-caregory
$ids = Category::select('id')->where('parent_id', $category_id)->get();
$array = array();
foreach ($ids as $id)
{
$array[] = $id->id;
}
return Item::whereIn('category_id', $array )->count();
}
else
{
return Item::where('category_id', $category_id)->count();
}
}
In the controller
$Category = new Category;
$allCategories = $Category->getCategories();
return view('home', compact('allCategories'));
And in my blade
#foreach($allCategories as $category)
<div class="card-body">
<h4 class="card-title">{!!$category->title!!} <span class="badge badge-primary badge-pill">({!! $category->itemCount !!})</span></h4>
</div>
#endforeach
itemCount is (0) when item is in Parent category.
After Discution that we had it's a simlpe query that you want just do it like this :
public function getItemCount( $category_id)
{
return Item::where('category_id', $category_id)->count();
}
Or if you have a relationship you can simply do it like this :
public function addRelation( $categoires )
{
$categoires->map(function( $item, $key)
{
$sub = $this->selectChild($item->id);
$item->itemCount = $item->items()->count();
return $item = array_add($item, 'subCategory', $sub);
});
return $categoires;
}

How to create a nested-list of categories in Laravel?

How can I create a nested list of categories in Laravel?
I want to create something like this:
--- Php
------ Laravel
--------- Version
------------ V 5.7
--- Python
------ Django
--- Ruby
..........
The fields of my categories table are:
id | name | parent_id
If I have to add another column like depth or something, please tell me.
I am using this following code, but I think it is not the best solution. Besides, I can not pass this function to my view.
function rec($id)
{
$model = Category::find($id);
foreach ($model->children as $chield) rec($chield->id);
return $model->all();
}
function main () {
$models = Category::whereNull('parent_id')->get();
foreach ($models as $parent) return rec($parent->id);
}
You can make a self-referential model:
class Category extends Model {
public function parent()
{
return $this->belongsTo('Category', 'parent_id');
}
public function children()
{
return $this->hasMany('Category', 'parent_id');
}
}
and make a recursive relation:
// recursive, loads all descendants
public function childrenRecursive()
{
return $this->children()->with('childrenRecursive');
}
and to get parents with all their children:
$categories = Category::with('childrenRecursive')->whereNull('parent_id')->get();
Lastly you need to just iterate through the children until children is null. There can definitely be some performance issues with this if you are not careful. If this is a fairly small dataset that you plan to remain that way it shouldn't be an issue. If this is going to be an ever growing list it might make sense to have a root_parent_id or something to query off of and assemble the tree manually.
This function will return tree array:
function getCategoryTree($parent_id = 0, $spacing = '', $tree_array = array()) {
$categories = Category::select('id', 'name', 'parent_id')->where('parent_id' ,'=', $parent_id)->orderBy('parent_id')->get();
foreach ($categories as $item){
$tree_array[] = ['categoryId' => $item->id, 'categoryName' =>$spacing . $item->name] ;
$tree_array = $this->getCategoryTree($item->id, $spacing . '--', $tree_array);
}
return $tree_array;
}
If someone needs a better answer look up my answer, it helped me, when I had faced with such a problem.
class Category extends Model {
private $descendants = [];
public function subcategories()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function children()
{
return $this->subcategories()->with('children');
}
public function hasChildren(){
if($this->children->count()){
return true;
}
return false;
}
public function findDescendants(Category $category){
$this->descendants[] = $category->id;
if($category->hasChildren()){
foreach($category->children as $child){
$this->findDescendants($child);
}
}
}
public function getDescendants(Category $category){
$this->findDescendants($category);
return $this->descendants;
}
}
And In your Controller just test this:
$category = Category::find(1);
$category_ids = $category->getDescendants($category);
It will result ids in array all descendants of your category where id=1.
then :
$products = Product::whereIn('category_id',$category_ids)->get();
You are welcome =)
Searching for something somehow in this area I wanted to share a functionality for getting the depth level of child:
function getDepth($category, $level = 0) {
if ($category->parent_id>0) {
if ($category->parent) {
$level++;
return $this->getDepth($category->parent, $level);
}
}
return $level;
}
Maybe it will help someone!
Cheers!
you can solve this problem like this :
class Category extends Model
{
public function categories()
{
return $this->hasMany(Category::class);
}
public function childrenCategories()
{
return $this->hasMany(Category::class)->with('categories');
}
}
and get category with children like this :
Category::whereNull('category_id')
->with('childrenCategories')
->get();
notice : just rename parent_id column to category_id
This also worked:
View:
$traverse = function ($categories) use (&$traverse) {
foreach ($categories as $category) $traverse($cat->Children);
};
$traverse(array ($category));
Model:
public function Children()
{
return $this->hasMany($this, 'parent');
}
public function Parent()
{
return $this->hasOne($this,'id','parent');
}

Retrieve Related Models using Laravel 4

I have 3 tables: Users, Projects, Items and Friends. I want to get all the items for a project and list each item with related friends. Am I missing something in my model? Ultimately I want to get all the friends which are related to the items.
// CONTROLLER
public function show($id)
{
$uid = Auth::user()->id;
$projects = Project::find($id)->with('item.friend')->get();
return View::make('projects.show', compact('projects'));
}
//VIEW
#foreach ($projects as $project)
#foreach ($project->friend as $friend)
<li>
<a href="#" class='itemLink' >{{$friend->email}}</a>
<a href="#" class='itemLink' >{{$projects->item->name}}</a>
</li>
#endforeach
#endforeach
// MODELS
class User extends Eloquent {
public function project()
{
return $this->hasMany('Project');
}
public function item()
{
return $this->hasMany('Item');
}
public function friend()
{
return $this->hasMany('Friend');
}
class Project extends Eloquent {
protected $guarded = array();
public static $rules = array();
public function item()
{
return $this->hasMany('Item');
}
public function friend()
{
return $this->hasMany('Friend');
}
class Item extends Eloquent {
protected $guarded = array();
public static $rules = array();
public function friend()
{
return $this->hasMany('Friend');
}
class Friend extends Eloquent {
protected $guarded = array();
public static $rules = array();
You are missing a loop. I recommend that when setting up a many to many relationship, make sure your method is plural. It makes a lot more sense when trying to read the code. You'd then send up with #foreach $project->items as $item or $item->friends as $friend.
#foreach ($projects as $project)
#foreach ($project->item as $item)
#foreach($item->friend as $friend)
<li>
<a href="#" class='itemLink' >{{$friend->email}}</a>
<a href="#" class='itemLink' >{{$item->name}}</a>
</li>
#endforeach
#endforeach
#endforeach
Your models seems OK at first look. I think since you are pulling item.friend relationship, this line
#foreach ($project->friend as $friend)
should be,
#foreach ($project->item->friend as $friend)

Categories