Laravel - Query builder get relation - php

In my project I have a Polymorphic relation. This relation is as followes:
Plugins
id - integer
composer_package - string
Themes
id - integer
composer_package - string
Products
id - integer
name - text
...
commentable_id - integer
commentable_type - string
When I need to display all the products that are a Theme I do the following:
$themes = DB::table('products')->orderBy('id', 'asc')->where('productable_type', Theme::class)->get();
The code above provides me with a list of all the products where the productable_type field is filled with App/Theme, I display them with a foreach loop like this #foreach($themes as $theme). Now my problem is.
I need to get the composer_package from the themes table that belongs to that product. So say for instance. I get a product from the products table where the productable_id is 3. I want the value of the composer_package field in the themes table where the ID is 3. When I do {{ $theme->products->composer_package }} or {{ $theme->composer_package }} It gives me the error Undefined property What is causing this?
This is my product model
public function product()
{
return $this->morphTo();
}
public function order_items()
{
return $this->hasMany(Orderitems::class);
}
And this is my theme model
public function webshops()
{
return $this->hasMany(Webshop::class);
}
public function products()
{
return $this->morphMany(Product::class, 'productable');
}
Thanks in advance!

Is there any reason you are using the DB facade to pull data instead of the actual models?
One issue your are having is that your polymorphic relationship is stored in the commentable_id / commentable_type field, while your function name is product. Laravel does some of its magic just by naming alone, so let's make sure the column names and polymorphic function names match up in the product model:
public function commentable()
{
return $this->morphTo();
}
After that, try something like this in the controller:
$themeProducts = Product::where('commentable_type', Theme::class)->get();
And this in the view
#foreach ($themeProducts as $themeProduct)
{{ $themeProduct->commentable->composer_package }}
#endforeach

Your table has "commentable", but your model is looking for "productable". You're also missing a method.
in your product model add:
public function commentable(){
return $this->morphTo();
}
in your theme model modify the products() method to:
public function products(){
return $this->morphMany(Product::class, 'commentable');
}
You could also update your commentable_type and commentable_id columns in your table to productable_type and productable_id, in which case you need to rename commentable() in the code for the product model above to productable(), and 'commentable' to 'productable' in the code for the theme model above.
(Source: https://laravel.com/docs/5.6/eloquent-relationships#polymorphic-relations)

Create relations with the model as mentioned in above answers, then you can retrieve relations using with helper.
E.g.:
Product::where('commentable_type', Theme::class)->with('commentable')->get();
and then can directly use as mentioned above.
e.g.: $themeProduct->commentable

Related

In Laravel Eloquent how to define relationship through secondary table? (Always returning 0 relations)

I feel like this should work. I have a list of products and categories (types).
Tables:
Products
- id
- name
- etc
Types
- id
- name
- etc
ProductTypes
- product_id
- type_id
Now, I feel like in the Type model in Laravel, I should be able to define this relationship:
public function products()
{
return $this->hasManyThrough(Product::class, ProductType::class, 'type_id', 'id');
}
I've tried other variations with the secondary ids in the additional parameters but no luck, always an empty list. Is ProductTypes a pivot table and therefore should be dealt with differently?
Edit: What's weird is that for the final 2 parameters ($localKey = null, $secondLocalKey = null) even if I enter complete garbage no error is thrown but these 2 parameters $firstKey = null, $secondKey = null have to be correct).
You are using the wrong relationship. Based on your database structure, a product can belong to many type. Therefore, it should be a BelongsToMany instead of a HasManyThrough.
You can achieve what you want with the following method, by passing the table name of your ProductTypes as the second parameter:
public function products()
{
return $this->belongsToMany(Product::class, 'product_types');
}
If your ProductType model extends Illuminate\Database\Eloquent\Relations\Pivot, you can do:
public function products()
{
return $this->belongsToMany(Product::class, 'product_types')
->using(ProductType::class);
}
For more information about Many to Many relationships: https://laravel.com/docs/6.x/eloquent-relationships#many-to-many

Laravel 5 Inverse of belongsToMany

actually not sure if I am doing it correctly or there are other ways. What I have in my database are the following
brands
id: integer
name: string
slug: string
description: string
timestamp
products
id: integer
name: string
price: decimal
slug: string
description: string
timestamp
brand_product
id: integer
brand_id: unsignedInteger
product_id: unsignedInteger
Basically, brands have many products, so on my Brand model
...
public function products()
{
return $this->belongsToMany(Product::class);
}
...
However, a Product has one (1) Brand model. Pretty sure I can achieve what I want by adding brand_id on my products table and do the relationship thing on my Product model. However, I have an above database structure. What I did, on my Product model:
...
protected $appends = ['brand'];
public function brand()
{
return $this->belongsToMany(Brand::class);
}
public function getBrandAttribute()
{
return $this->brand()->first();
}
...
Your assumption of adding a brand_id on products is correct. What you are describing is a one-to-many relationship, but a database structure representing a many-to-many. Remove the pivot table and add the brand id to the products table and you'll be good to go.
// Product model
public function brand()
{
return $this->belongsTo(Brand::class);
}
// Brand model
public function products()
{
return $this->hasMany(Product::class);
}
https://laravel.com/docs/5.5/eloquent-relationships#one-to-many
The best approach would be to remove the pivot table and define a one-to-many relation.
However, if you have to keep the schema with the pivot table, you just simply put the belongsToMany() on both of your models. There is no specific inverse of belongsToMany() in Laravel.
https://laravel.com/docs/5.5/eloquent-relationships#many-to-many
Add the following code inside your Product model:
public function brands()
{
return $this->belongsToMany('App\Brand', 'brand_product');
}
Now you can get the first row of the brands of the product by using the following code in your controller:
$product->brands()->get()->first(); // Will return the first row

Laravel Eloquent get a relation that matches a value in pivot table

I need to get model that must match with the value stored in a pivot table, but unfortunately i couldn't get the solution.
Here is my schema
PEROPERTY TABLE
id
FILTER TABLE
id
FILTER_OPTION TABLE
id
filterId
FILTER_OPTION_TRANSLATE TABLE
optionId
languageId
title
PROPERTY_FILTER TABLE
propertyId
filterId
optionId
What i wanto to do is:
#foreach($property->filters as $filter)
{{ $filter->option->translate->title }}
#endforeach
but here the problem for me is how to say get option matches optionId in PROPERTY_FILTER TABLE
My models:
PROPERTY MODEL
public function filters()
{
return $this->belongsToMany(Filter::class, 'PROPERTY_FILTER','propertyId','filterId');
}
FILTER MODEL
public function option()
{
return $this->hasMany(Filter_Option::class, 'filterId');
}
FILTER OPTION MODEL
public function translate()
{
return $this
->hasOne(Filter_Option_Translate::class, 'optionId')
->where('langId', currentLanguage()->langId);
}
I hope i can get some help, thanks from now.
I solved my problem by using pivot table as seperated model.
I was trying to get 3 level far relation through pivot table but even intermediate model couldn't solve my problem, and i just tried a seperated model.
Firstly, i created Property_Filter model that represents property_filter pivot table and I added filter and option methods as below shown:
public function filter()
{
return $this->belongsTo(Filter::class, 'filterId');
}
public function option()
{
return $this->belongsTo(Filter_Option::class, 'filterValue');
}
Then converted filters relationship method
public function filters()
{
return $this->belongsToMany(Filter::class, 'PROPERTY_FILTER','propertyId','filterId');
}
to
public function filters()
{
return $this->hasMany(Property_Filter::class,'propertyId');
}
Now i reach the filter and option selected for iterated filter like below shown:
#foreach($properties as $property)
#foreach($property->filters as $filter) // here i get filters chosen for iterated property
<span class="caption">{{ $filter->filter->translate->entryTitle }}</span> // here i get iterated filters title (translated)
<span class="value">{{ $filter->option->translate->entryTitle }}</span> // here i get chosen option for iterated filter not all options belognsto iterated filter
#endforeach
#endforeach

Query foreign key data from Blade template

I have two models: MenuCategory and MenuItem, I want to display MenuItem data on my blade page along with its MenuCategory. I know its possible to do this by adding it to the return data in my controller however I would like to do it leveraging Eloquent instead, however I receive errors.
Here are my codes:
MenuCategory model
public function items()
{
return $this->hasMany('App\MenuItem');
}
MenuItem model
public function category()
{
return $this->belongsTo('App\MenuCategory');
}
Controller
public function show($id)
{
$item = MenuItem::findOrFail($id);
return view('menu.admin.single', compact('item'));
}
Blade Page
{{ $item->category->name }}
UPDATE:
Table menu_item
id
name
menu_category_id
Table menu_category
id
name
When using all the above I get the following error:
Trying to get property of non-object
This error is due to the naming convention of Eloquent.
Provide the optional foreign key variable in your relationship method to make it work, ie.
$this->belongsTo('App\MenuCategory', 'menu_category_id');
Probably every Item doesn't contain a related category but to make sure you may try something like this, it'll try to retrieve the name only if there is a related category is available:
{{ $item->category ? $item->category->name : 'No Name or empty string' }}
Alternatively you may try something like this:
$item = MenuItem::has('category') // check if there is a related category
->with('category') // if yes then load it with that category
->findOrFail($id);
You used a different foreign key than Laravel expect so explicitly mention it like:
public function category()
{
return $this->belongsTo('App\MenuCategory', 'menu_category_id', 'id');
}

Laravel 4 Eloquent/Model Relationships

I am setting up several Models an want to know the correct approach to table structure and Model relationships.
Let's assume we have a shop containing products, each with properties size and color.
Table products
id
size_id
color_id
price
Table sizes
id
name
Table colors
id
name
Models
class Product extends Eloquent {
public function size() {
return $this->hasOne('Size', 'id');
}
public function color() {
return $this->hasOne('Color', 'id');
}
}
class Size extends Eloquent {
public function products() {
return $this->belongsTo('Product', 'size_id');
}
}
class Color extends Eloquent {
public function products() {
return $this->belongsTo('Product', 'color_id');
}
}
This way I can easily echo the color/size of a product using {{ Product->size['name'] }}. Also, I want to pass Eloquent the size's foreign key size.id like Product::where('size_id', '5') rather than its name size.name.
Problem: Doing $products = Product::has('size', '=', '5')->get() does not give me any results, yet doing $products = Product::where('size_id', '5')->get() does.
I am pretty confused, what went wrong?
I think that the problem is that your ::has() method is looking for products with exactly 5 different sizes on each specific product, which would assume that you would be using $this->hasMany('Size') in your Product model. Where as the ::where() method is returning results where the size of the product is 5.
In the documentation they use an example of comments. A post will have a list of comments. You can find posts that have at least one comment (ie. Post::has('comments')->get()) or you can find posts that have more than 3 comments (ie. Post::has('comments', '>=', '3')->get()).
http://laravel.com/docs/eloquent#querying-relations

Categories