Iterating through collection - laravel - php

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); } });

Related

How to create JSON tree from related objects?

I am trying to create Laravel/Vue project with two models: Category and Article. Vue part haves tree-view, which will display categories and articles tree. Categories may belong to another categories, Article may belong only to Article.
How can i form json tree from these relations?
model Category
public function articles() {
return $this->hasMany(Article::class);
}
public function childs() {
return $this->hasMany(Category::class)->union($this->files()->toBase());
}
but it shows The used SELECT statements have a different number of columns, because there is defferent fields in results.
One solution i see here is to find every article and post and create array, then jsonify it. Maybe any better solutions?
UPDATE
Done it with this code (in api controller):
public function nodes() {
$rootCategories = Category::where('category_id', null)->get();
$out = $this->_nodes($rootCategories);
return response()->json($out);
}
private function _nodes($eCategories) {
$out = [];
foreach($eCategories as $cat) {
$out[$cat->id] = $cat->toArray();
$out[$cat->id]["type"] = "folder";
$out[$cat->id]["childs"] = [];
foreach ($cat->articles as $article) {
$out[$cat->id]["childs"][$article->id] = $article->toArray();
$out[$cat->id]["childs"][$article->id]["type"] = "article";
}
if ($cat->categories) {
$out[$cat->id]["childs"] = $out[$cat->id]["childs"] + $this->_nodesCategory($cat->categories);
}
}
return $out;
}

How to get the count and values of item in a subcategory with parent_id in Laravel

How do I get the count and values of all items within a subcategory that has a parent_id. I have a category model which is related to the product model. And in the category table, there are parent_ids and their subcategories. How do I return all the products belonging to a parent category?
The database is shown below
Database
The Relationships are shown here
public function products()
{
return $this->hasMany('App\Product');
}
public function category()
{
return $this->belongsTo('App\Category');
}
Then in my controller, this returns 0 as count
public function niche($url)
{
$niche = Category::where('url',$url)->first();
$categories = Category::where('parent_id',$niche->id)->get();
$products = [];
foreach($categories as $category) {
$products = Product::where('category_id',$category->id)->get();
}
dd($products->count());
}
Output
Please how do I get the number of products belonging to this niche?
Thanks.
I think products() is a relationship method of products and categories
Can you try like this:
$products = [];
foreach($categories as $category){
array_push($products, $category->products());
}
dd(count($products, true));
I think this is happening because every time you go through a loop you create a new product variable, thus it will always dump out the count of the last category in the loop.
You should try something like this:
public function niche($url){
$niche = Category::where('url',$url)->first();
$categories = Category::where('parent_id',$niche->id)->get();
$products = [];
foreach($categories as $category){
array_push($products,$category->products());
}
dd($products->count());
}
I think the result is 0 maybe because the last sub category doesn't have any product.
Btw, I suggest a bit cleaner approach in this base. Lets define a sub categories relationship for a category.
class Category extends Model
{
public function products()
{
return $this->hasMany('App\Product');
}
public function subCategories()
{
return $this->hasMany(static::class, 'parent_id');
}
}
So you are able to access the products and the number of products in a number of ways:
// You can eager load for performance.
$category = Category::with('subCategories.products')->where('url', $url)->first();
$products = $category->subCategories->flatMap->products;
$count = $products->count();
// Or you can eager load JUST the number of products.
$category = Category::withCount('subCategories.products')->where('url', $url)->first();
$numberOfProducts = $category->products_count;
You have to use withCount on children relationship.
$categories = Category::whereNULL('parent_id')->with(['categories' => function ($query) {
$query->withCount('products');
}])->get();
Hopefully should be work

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');
}

Laravel - Check if in many to many relationship

A user can save ingredients to a shopping list. Now, when a User is logged in and visit a recipe, the shopping list widget is showing him, which ingredients he already have on his list, and that's my problem.
Here I'm getting all ingredients for the recipe:
$recipe = Recipe::find($id);
$recipe->load('ingredients');
Working fine with a foreach on $recipe->ingredients
And here I'm gettingthe shopping list a user has for this recipe, if he has one:
if(Auth::check()) {
$list = ShoppingList::with('ingredients')->where('recipe_id',$id)->where('user_id',Auth::user()->id)->get();
//...
}
And here I'm trying to check, if an saved ingredient is on the shopping list:
foreach($recipe->ingredients as $i) {
foreach($list as $l) {
$ingredient = Ingredients::where('id','=',$l->ingredients_id)->get();
$i->isAdded = (count($ingredient) > 0) ? 1 : 0;
}
}
But somehow that's totally wrong. What am I missing?
Relationships:
Recipe:
public function user() {
return $this->belongsTo('User','user_id');
}
public function lists() {
return $this->hasMany('ShoppingList','recipe_id');
}
public function ingredients() {
return $this->belongsToMany('Ingredients','ingredients_recipe','recipe_id','ingredients_id')->withPivot('amount');
}
Shopping List:
public function user() {
return $this->belongsTo('User','id');
}
public function recipe() {
return $this->belongsTo('Recipe','recipe_id');
}
public function ingredients() {
return $this->belongsToMany('Ingredients','shopping_list_ingredients','shopping_list_id','ingredients_id')
->withPivot(array('unit','amount'))
->withTimestamps();
}
Ingredients:
public function recipes() {
return $this->belongsToMany('Recipe','ingredients_recipe','recipe_id','ingredients_id')->withPivot('amount');
}
public function lists() {
return $this->belongsToMany('ShoppingList','shopping_list_ingredients','shopping_list_id','ingredients_id')
->withPivot(array('unit','amount'))
->withTimestamps();
}
What am I doing wrong? Thank you!
Your explanation is a bit cloudy with this code, but the point is, that you need to find missing ingredients and add them to the shopping list, is it correct?
For this you can use Collection's diff method:
// retrieve collection of the ingredients for a recipe
$recipe = Recipe::with('ingredients')->find($recipeId);
// retrieve collection of the ingredients for a shopping list
$list = ShoppingList::with('ingredients')->find($recipeId);
// find the ingredients for the recipe that are not on the list already
// return Collection of Ingredient models
$missingIngredients = $recipe->ingredients->diff($list->ingredients);
// and ingredients on both list and recipe
$missingIngredients = $recipe->ingredients->intersect($list->ingredients);

Codeigniter: Query for looping through two results

I am trying to loop through two joined tables of data. One table is an image collection and another is the images. The images have a foreign key to collection.
My question is, how do I achieve the following in my view?
foreach ($collections as $collection) {
echo '<ul>';
foreach ($collection->image as $image) {
echo '<li><img src="'.$image->url.'" /></li>';
}
echo '</ul>';
}
I am currently using this in the controller:
class Collection extends CI_Controller {
public function index()
{
$this->load->model('Collection_model');
$data['collections'] = $this->Collection_model->get_latest_collections();
$this->load->view('collection_view.php', $data);
}
}
And have the following model:
class Collection_model extends CI_Model {
function get_latest_collections()
{
$this->db->select('*');
$this->db->from('photo');
$this->db->join('collection', 'photo.collection_id = collection.id');
$this->db->order_by("collection.date_created", "DESC");
$query = $this->db->get();
return $query->result();
}
}
The problem with the above is that when I loop through the collection results I am actually looping directly through all the images. I am having to put some logic into the view to check to see if the collection id has changed to put in the . This means that I can't use next() and prev() to get the next and previous collection as the loop is looping through the images and next() and prev() gives the next and previous image rather than the next and previous collection.
If i well understood your question, you want loop on your photos and organize them by collections.
There are several ways to achieve that, but this cannot be by a join query because the tables relation is one(collection) to many (photos).
Solution 1: You want show all your photos
//get all collections
$collections = $this->db
->order_by("date_created", "DESC")
->get('collection')
->result();
//get all photos
$photos = $this->db
->get('photo')
->result();
Solution 2: you want show some collections
//get some collections
$collections = $this->db
//->where('..', '..') //some filtering
->order_by("date_created", "DESC")
->get('collection')
->result();
//extract ids
$collection_ids = array();
foreach($collections as $collection)
{
$collection_ids[] = $collection->id;
}
//get photos who are in these collections
$photos = $this->db
->where_in('collection_id', $collection_ids)
->get('photo')
->result();
In your view
The two solutions above work with this code.
//loop on collections
foreach($collections as $collection)
{
//<ul>..
foreach($photos as $photo)
{
if($photo->collection_id == $collection->id)
{
//<li>..$photo->url..
}
}
//</ul>..
}
Or to have exactly what you expected in your first block of code
//loop on collections
foreach($collections as $collection)
{
$collection->images = array();
foreach($photos as $photo)
{
if($photo->collection_id == $collection->id)
{
$collection->images[] = $photo;
}
}
}
//so in your view (what you expected)
foreach($collections as $collection)
{
//<ul>..
foreach($collections->images as $image)
{
//<li>..$image->url..
}
//</ul>..
}
But this last code implies to loop twice.

Categories