I am trying to mix pivot table values in Laravel - php

I have three table category and products with its pivot table as category_product
One category has many products
I want the category name in attributes of products with its own field.
$saijal = "Our Products";
$products = Product::orderBy('created_at','DESC')->with('categories')->get();
$abc = new Collection();
foreach($products as $pro)
{
$abc->put($pro->id,$pro);
}
foreach($products as $k => $pro)
{
$abc->get($pro->id)->children = new Collection();
$abc->get($pro->id)->categoryName= $pro->categories->name;
$abc->get($pro->id)->children->push($pro);
unset($pro[$k]);
}
dd($abc);
For example:

If I'm understanding correctly, you want to be able to access an attribute called "categoryName" directly from the product object. In order to do this, simply set up an attribute getter in the Product.php model that looks like this:
public function getCategoryNameAttribute()
{
return $this->category->name;
}
Then you can simply reference the category name like this:
$product->categoryName
The main issue I see is that you are referring to "categories" in your code as if it is plural. If the product belongs to many categories, the solution would be different and your relationships would require a pivot table, as you describe. But if the product just belongs to one category, as your code implies, the solution above should be sufficient and you actually do not need a pivot table. You would just have a category_id column directly in the products table.
If you do want to have multiple categories for each product, you could do something similar but return an array:
public function getCategoryNamesAttribute()
{
return $this->categories->pluck('name');
}
This would return an array of category names associated with this Product, and would be accessible as:
$product->categoryNames

You can add custom attributes to appends array (not attributes array).
class Product extends Model
{
protected $appends = ['category_names'];
public function getCategoryNamesAttribute()
{
return $this->categories->map->name;
}
}
you can access this attribute as,
$product->category_names;
Note
The benefit of adding this to the appends array is. If you don't add this to the appends array, When you call toArray() or toJson() or send this $product via json. you loses this attribute.

Related

Property [group] does not exist on this collection instance

I have retrieved an instance of a product item at blade, so when I do dd($product), I get this result:
And the Product Model is connected to GroupProduct Model like this:
public function groupproducts()
{
return $this->hasMany(GroupProduct::class,'product_id','id');
}
So at group_product table, the product has a custom group_id like this:
So when I do dd($product->groupproducts); I get this result properly:
Then at the Model GroupProduct, I've added these two relationships:
public function product()
{
return $this->belongsTo(Product::class,'product_id','id');
}
public function group()
{
return $this->belongsTo(Group::class,'group_id','id');
}
Now I need to access the name of the group that comes with the retrieved product_id, but when I try this:
dd($product->groupproducts->group->name);
I get this error:
Property [group] does not exist on this collection instance.
However the relationships seems to be applied and it should be showing the name of group...
So what's going wrong here? How can I fix this issue?
UPDATE #1:
Controller:
public function addAttribute(Product $product)
{
return view('admin.products.addAttribute', compact('product'));
}
$product->groupproducts is a collection(array) of products.
You can do:
dd($product->groupproducts->first()->group->name);
This will get first element from collection which is instance of Group class.
Your relation from your product model to the group products model is a one to many. This means, that even if there's only one result, you will be receiving a collection.
You can see in your own screenshot that the return value of $product->groupproducts is an instance of a collection with 1 item.
If you only ever want the first, you should change your relation to a one to one.
However, if you just want the first in this particular case, you should call for the first on the query instance, not on the collection instance.
So $product->groupproducts()->first()->group->name. This way you don't load x amounts, but instead only 1, from your database, which is much faster.
you have to use nested relationship:
1- you can use it in your Model via:
public function groupproducts()
{
return $this->hasMany(GroupProduct::class,'product_id','id')->with('group');;
}
2- you can use it in your controller:
Products::with('groupproducts.group')->get()
REFRENCE

Create conditional in Model methods

Hi guys i wish to create a custom method on my Category Model but i need to create some conditionals with it.
table post:
- id;
- category_id
-name...
table Category:
- id
- parent_id
- name
Imagin that i have a post, and in this post there is a category called SomeSubcategoryName, i my posts table i have a column called category_id.
When i call the post record i have a relation with the Category Model, but i wish to have in my Category Model a method called masterCategory, where i give the mster category by checking if the parent_id is null or not, in case that the record have a parent_id of null great, i return the record but if is not i need to use the parent_id value and search on the Model category column id for the record and return the result.
Imagine this scenario:
$post = Post::find(2);
//this call should return the main category and not the subcategory info.
$post->masterCategory();
In your category model define a relationship on its self:
public function masterCategory()
{
return $this->belongsTo(App\Category::class, 'parent_id', 'id');
}
Eager load the relationships on your Post when you query it:
$post = Post::with(['category', 'category.masterCategory'])->firstOrFail($id);
Access it like this:
$post->category->masterCategory; // will be the mastery category or null
Otherwise us:
$post->category;
Don't over complicate it.
In your Category model you should have something like this
public function master()
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function isMaster()
{
if($this->parent_id)
return false;
else
return true;
}
Now you can check that post's category is master or not :
if($post->category->isMaster())
....
The second way is using relations and eloquent
$post = Post::with(['category', 'category.master'])->first($id);

Laravel - Access subcategory from product (belongsTo) in ajax response data.

One products has one subcategory. In my products table I have subcategory_id field. I have also set-up a belongsTo relationship between Product and Subcategory models.
So I have a method which returns all products with a certain tag id.
Here is my code:
public function getProductsByTag($tag_id)
{
$tag = Tag::find($tag_id);
$products = $tag->products; //belongsTo relationship
return json_encode(['products' => $products]);
}
Then on success in my ajax request I need to access the subcategory of the product like I accessed the products of the tag $tag->products. So in Laravel it would be:
$subcategory = $product->subcategory;
I thought that product.subcategory would do the trick but I get undefined. Here is my ajax success function:
success: function (data) {
$.each(data.products, function (i, product) {
console.log(product.subcategory);
});
},
I get undefined in my console. How can I access relationships in ajax response data?
I found a solution by providing a variable that holds the relationship inside the model I need. I add one variable for subcategory in my Product model and one variable for category in my Subcategory model. Here is what it looks like:
In my Product model:
protected $with = array('subcategory');
In my Subcategory model:
protected $with = array('category');
Now I can access them easily from my ajax success response data.
As I said in my comment, laravel doesn't load automatically all the relations so you have to do that.
if you would like to load every time subcategories into your products so you have to update you model and add a with attribute like so :
//Product model
protected $with = ['subcategory'];
Or if you just want to do this just once, so you have to do something like :
$products = $tag->products()->with('subcategory')->get();

Laravel 4: many to many (insertion)

I have these tables in DB:
[posts, cats (categories), posts_cats (pivote)]
the relation between posts table and cats is many to many
I declared the relation in the models classes:
//Post.php
public function cats()
{
return $this->belongsToMany('cats');
}
//Cats.php
public function post()
{
return $this->belongsToMany('posts');
}
the question is, How to insert new post with multiple categories?
thanks,
Let's say you know the id of the post then you can attach a single cat like this:
Post::find($post_id)->cats()->attach($cat_id);
Or attach multiple cats like this:
$cat_ids = array(1,2,3,4);
Post::find($post_id)->cats()->attach($cat_ids);
If you got the Post model object in a variable, lets say $post:
$post->cats()->attach($cat_id);
// Or with multiple
$cat_ids = array(1,2,3,4);
$post->cats()->attach($cat_ids);
If you have a single category as an model object in, lets say $model:
$post->cats()->save($model);
Watch out with #Gadoma's answer. Its not wrong, but if you want to add categories to an post that already has categories then you should use attach() instead of sync(). Sync() will delete all others that are not provided to it when used.
edit:
So if you are creating a new Post then you probably are doing something like this:
$post = new Post;
$post->title = 'The title';
$post->something_else = 'Lorem';
$post->save();
//So now you have both the model object (the $post variable) and the id ($post->id).
$post->cats()->attach($cat_ids);
When you insert the post, then itterate over the categories and attach them to the new post. Something like that:
// $categories is an array of the categories to attach
foreach ($category_id in $categories) {
// Get a category object
$category = CategoryModel::find($category_id);
// $post is the new post
$post->cats()->attach($category);
}
I hope it helps you.
From the docs http://laravel.com/docs/eloquent#inserting-related-models
Inserting Related Models (Many To Many)
[...]
You may also use the sync method to attach related models. The sync
method accepts an array of IDs to place on the pivot table. After this
operation is complete, only the IDs in the array will be on the
intermediate table for the model:
And a code example:
$post = new Post(array('field1'=>'value1','fieldN'=>'valueN')) //example create new post
$categoryIds = array(1,3,4,5); //ids of (cats) categories you want the post to go into
$post->cats()->sync($categoryIds); //synchronise pivot table content with $categoryIds

How can I combine two tables with my Eloquent query?

We work on a database where table names are named locally.
table.product
-- id
-- picture
-- category_id
table.category
-- id
-- caterory_name
-- category_directory
There are some products in table. product.category_id should point category.id so system will understand that product belongs to desired table.
We could do this by using INNER JOIN but we can't do it with Laravel. We probably need to configure our models with has/belongs_to etc.
This is where we struggle.
//Controller
$products = Product::all();
return View::make('theme-admin.product_list')
->with('products', $products);
//View (in foreach)
{{ URL::to('uploads/categories/[NEED_CATEGORY.CATEGORY_DIRECTORY_HERE]/' . $products[$k]->picture) }}
We can't get Category.category_directory info in our views since we pass Product::all() only.
How can we make it so the $products array also contains category.category_directory for each value and we can access it like; $products[$k]->category_name?
Thanks!
Create a category relation in your Product model:
class Product extends Eloquent {
private $table = 'myProductsTableName';
public function category()
{
return $this->belongsTo('Category', 'myCategoryColumnID');
}
}
class Category extends Eloquent {
private $table = 'myCategoriesTableName';
}
Unless you need to select all products from a particular category, you don't need to create a hasMany relation on your Category model.
And you just need to use myCategoryColumnID if your products table doesn't follow the singular_table_name_id rule (product_id).
Then just use it:
//View (in foreach)
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/' . $products[$k]->picture) }}
I would setup a relationship...
class Product extends Eloquent {
public function category()
{
return $this->belongsTo('Category');
}
}
class Category extends Eloquent {
public function product()
{
return $this->hasMany('Product');
}
}
The name you use in the realtionship is the Model name.....so be sure if you're using a different table name than your model name, that you set that in your model
protected $table = "yourtablename";
Then use it like so...
{{ URL::to('uploads/categories/'.$products[$k]->category->category_directory.'/'
You still end up querying the database multiple times this way... This is called the N+1 effect. Example, if you have 5 products, one query will be executed to get those products. Then in the loop we are executing a query to get the category. This results in 6 total queries.
To solve this problem, use eager loading, which reduces those 6 queries in our example down to 2.
$products = Product::with('category')->get();
Then send that to your view, where you can do your foreach loop..
foreach($products as $val){
//To output the directory
echo $val->category->category_directory;
}
Or in blade...
#foreach($products as $val)
{{URL::to('uploads/categories/'.$val->category->category_directory.'/'.$val->picture)}}
#endfor

Categories