How to create relationship between 3 models in laravel? - php

SQL scheme:
bulletins
id increment
deals
id increment
seller_id
buyer_id
deals_items - items = bulletins
id increment
title
desc
bulletin_id
deal_id
How can I get deal row by bulletin id? In raw SQL it looks like:
select `deals`.* from `deals` inner join `deals_items` on `deals_items`.`deal_id` = `deals`.`id` where `deals_items`.`bulletin_id` = 10572
I tried:
public function deals()
{
return $this->hasManyThrough(DealItem::class,Deal::class, 'bulletin_id','dealid','id');
}
But it seems a wrong way. Can't find right way in laravel doc about relation.
#HCK shows right way.
but when I doing $bulletin->deals() in blade template I got empty collection of deals.
When just $bulletin->deal - all is fine, we have collection of deals.
I using protected $with = ['deals'] in my bulletin model, but what is different call method or property? Why with method empty result?

#Amarnasan was close, but the order of the foreign keys was wrong. Try this:
Deal.php
public function bulletins()
{
return $this
->belongsToMany(Bulletin::class, 'deals_items', 'deal_id', 'bulletin_id')
->withPivot('title','desc');
}
Bulletin.php
public function deals()
{
return $this
->belongsToMany(Deal::class, 'deals_items', 'bulletin_id', 'deal_id')
->withPivot('title','desc');
}
From the docs:
As mentioned previously, to determine the table name of the
relationship's joining table, Eloquent will join the two related model
names in alphabetical order. However, you are free to override this
convention. You may do so by passing a second argument to the
belongsToMany method:
return $this->belongsToMany('App\Role', 'role_user');
In addition to customizing the name of the joining table, you may also
customize the column names of the keys on the table by passing
additional arguments to the belongsToMany method. The third argument
is the foreign key name of the model on which you are defining the
relationship, while the fourth argument is the foreign key name of the
model that you are joining to:
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
Update
When you access the relationship as a method: $bulletin->deals() you are accessing the relationship itself. This will return an instance of \Illuminate\Database\Eloquent\Relations\BelongsToMany (in your case). Here the query is not executed yet, so you could keep adding constrains to your query, for example:
$bulletin
->deals()
->where('seller_id', 45) // <---
->skip(5) // <---
-> ... (And so on)
When you access it as a dynamic property, you are already executing the query, so this will return a Collection instance. Is the same as calling the relationship as a method and then attach the ->get() at the end, so this two are equivalent:
$bulletin->deals()->get()
// equals to:
$bulletin->deals
Check this other answer, it answers your question.

DealClass:
public function bulletins()
return $this->belongsToMany('App\Bulletin', 'deals_items ', 'bulletin_id', 'deal_id')->withPivot('title','desc');
}
BulletinClass:
public function deals()
return $this->belongsToMany('App\Deal', 'deals_items ', 'deal_id', 'bulletin_id')->withPivot('title','desc');
}

deals model -
public function bulletins()
return $this->belongsToMany(Bulletin::class, 'deals_items ', 'bulletin_id', 'deal_id');
}
bulletin model:-
public function deals()
{
return $this
->belongsToMany(Deal::class, 'deals_items', 'deal_id', 'bulletin_id',);
}

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.6 OneToOne relation not working

I have a BuildingImage model with a OneToOne relation to BuildingType:
BuildImage Model:
/**
* Get the source type of the Building Image.
*/
public function type()
{
return $this->hasOne('App\BuildingType');
}
BuildingType Model:
/**
* Get the Building Image that owns the building type.
*/
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage');
}
My tables:
building_images table -> source is the building type id
building_types table
When I try to do this in my controller just to test:
(an ImageRequest has one or more Builings and a Building has one BuildingType)
$imageRequest = ImageRequest::findOrFail($id);
$buildings = $imageRequest->buildingImages;
foreach ($buildings as $building) {
dd($building->type);
}
I get this error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column
'building_types.building_image_id' in 'where clause' (SQL: select *
from building_types where building_types.building_image_id = 45
and building_types.building_image_id is not null limit 1)
What am I doing wrong here?
That's because by default laravel will look for a primary key named {model}_id, and given that you are using a different column name (source), you need to specify when defining the relationship:
As the documentation states:
Eloquent determines the foreign key of the relationship based on the model name. In this case, the Phone model is automatically assumed to have a user_id foreign key. If you wish to override this convention, you may pass a second argument to the hasOne method:
return $this->hasOne('App\Phone', 'foreign_key');
Additionally, Eloquent assumes that the foreign key should have a value matching the id (or the custom $primaryKey) column of the parent. In other words, Eloquent will look for the value of the user's id column in the user_id column of the Phone record. If you would like the relationship to use a value other than id, you may pass a third argument to the hasOne method specifying your custom key:
return $this->hasOne('App\Phone', 'foreign_key', 'local_key');
Now that that is clear. Let's talk about the relationship itself.
You are defining that a BuildImage has one BuildingType. But with that logic, the foreign key should be stored in the building_types table, and not the other way around (source column appears in the building_images table). And -I'm just assuming that- many BuildImage can belongs to an specific BuildingType. So, if this assumption is correct:
a BuildImage belongs to a specific BuildingType.
a BuildinType can be specify in many BuildImages
So, you should define your relationship like this:
BuildImage.php
public function type()
{
return $this->belongsTo('App\BuildingType', 'source');
}
BuildingType.php
public function images()
{
return $this->hasMany(BuildingImage::class, 'source');
}
Your BuildImage model should be
/**
* Get the source type of the Building Image.
*/
public function type() {
return $this->hasOne('App\BuildingType',"id","source");
}
And BuildingType Model should be
/**
* Get the Building Image that owns the building type.
*/
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage',"source","id");
}
This should work.
For more info have a look
https://laravel.com/docs/5.7/eloquent-relationships#one-to-one
Have you tried to indicate the index ID like this?
public function buildingImage()
{
return $this->belongsTo('App\BuildingImage', 'image_request_id');
}
Eloquent determines the foreign key of the relationship based on the
model name. In this case, the Phone model is automatically assumed to
have a user_id foreign key. If you wish to override this convention,
you may pass the second argument to the hasOne method:
return $this->hasOne('App\Phone', 'foreign_key');
https://laravel.com/docs/5.7/eloquent-relationships#one-to-one

Laravel relationship returns NULL

I'm try to get relations via laravel Eloquement model. I'm have two models ex Books.php and Magazine.php
in Books.php i have
public function magazines()
{
return $this->hasMany('App\BOOKS', 'id', 'id');
}
Then i'm try to return all magazines, which relate with books
$books = Book::find(123);
$magazines = $books->magazines()->get();
return $magazines;
but i'm have NULL magazines! When i'm add
$magazines = $books->magazines()->toSql();
i'm see this select: select * from "MAGAZINE" where "MAGAZINE"."ID" is null and "MAGAZINE"."ID" is not null - what is it? Why laravel model put "is null and is not null"?
Notice, if i'm change
public function magazines()
{
return $this->belongsToo('App\BOOKS', 'id', 'id');
}
Select would be like this: select * from "MAGAZINE" where "MAGAZINE"."ID" is null
Anyone know, what is this?
There are several problems in your code:
HasMany takes as first parameter the related model class so in your case is pointless to pass App\Book. You should pass App\Magazine assuming that your magazine model is called Magazine
you are not saying to the HasMany relationship what is the name of the foreign key on your magazines table.
That should do the job
public function magazines()
{
return $this->hasMany('App\Magazine', 'book_id', 'id');
}
IMHO i suggest you to read the Laravel documentation before posting here, there are a lot of examples that will explain to you how to work with relationships and database tables. Take a look to One to Many relationships.
EDIT
You can omit the ->get() on the relationship and simply fetch your magazines doing
$magazines = $books->magazines;
You should try this:
Please change and try:
public function magazines()
{
return $this->hasMany('App\MAGAZINE');
}

Get specified record from belongsToMany relation Laravel

If I have properties table, and 2 other tables:
*property_characteristics
- property_id (i.e. 1)
- characteristic_id (i.e. 5 - join with default_characteristics)
- value (i.e. 3 - aka 3 rooms)
*default_characteristics
- id (i.e. 5)
- name (i.e. rooms)
In the Property.php model I have:
public function characteristics()
{
return $this->belongsToMany('Proactiv\DefaultCharacteristic', 'property_characteristics', 'property_id', 'characteristic_id');
}
How can I get the number of rooms (value from property_characteristics) for a property starting from:
$property = Properties::find(1);
I would need something like this in view:
$property->characteristics->rooms // should return 3 which is the value columns on property_characteristics table
Since the value is on your pivot table, you need to tell Laravel about this extra field. Add to your belongsToMany line to make:
return $this->belongsToMany('Proactiv\DefaultCharacteristic', 'property_characteristics', 'property_id', 'characteristic_id')
->withPivot('value');
Then select characteristics with the name you want, rooms, and get the value:
echo $property->characteristics()->with('name', 'rooms')->first()->pivot->value;
Alternatively, add a getter to your Property model which does this for you (you'll still need to add that withPivot part to the relationship):
public function getRoomsAttribute()
{
return $this->characteristics()
->where('name', 'rooms')
->first()
->pivot
->value;
}
Then you can get the number of rooms in a similar way to how you originally wanted to, with $property->rooms.
Or you could generalize this to get any characteristic:
public function getCharacteristic($name)
{
return $this->characteristics()
->where('name', $name)
->first()
->pivot
->value;
}
And then get the number of rooms with $property->getCharacteristic('rooms').
First, you have to tell your relationship to make your additional field available. You do this using the withPivot() method:
public function characteristics() {
return $this->belongsToMany('Proactiv\DefaultCharacteristic', 'property_characteristics', 'property_id', 'characteristic_id')
->withPivot('value');
}
Now you can access your value on the pivot table. You do this like so:
$property = Properties::find(1);
foreach ($property->characteristics as $characteristic) {
echo $characteristic->pivot->value;
}
You can read more about this in the documentation here, under the Retrieving Intermediate Table Columns heading.

Laravel 5.2 hasManyThrough relationship issue

I have 3 tables: orders, codes, events
I want to be able to pull all events that an order has, but there's an intermediary table that acts as a pivot table. I've been trying to use hasManyThrough and belongsToMany (along with withPivot) without any luck.
Examples:
public function events()
{
return $this->belongsToMany('events'); // tried this, fails
return $this->hasManyThrough('events', 'codes'); // tried this, fails
return $this->hasManyThrough('events', 'codes', 'event_id', 'id'); // tried this, fails
}
Any pointers would be great!
That's a belongsToMany setup. First, the first parameter is the name of the related class. Second, since your pivot table doesn't follow the Laravel naming conventions, you need to specify the name of the pivot table in your relationship definition:
public function events()
{
// first parameter is the name of the related class
// second parameter is pivot table name
return $this->belongsToMany(Event::class, 'codes');
}
With this setup, you can do:
// get an order
$order = Order::first();
// has all the events related to an order
$events = $order->events;
There are many ways to do this. I will show a one you can get it done.
In Order.php model
public function codes(){
return $this->has('App\Http\Code');
}
In Code.php model
public function orders(){
return $this->belongsTo('App\Http\Order');
}
public function events(){
return $this->hasMany('App\Http\Event');
}
In Event.php model
public function codes(){
return $this->belongsTo('App\Http\Code');
}
Then in you Controller, call them to get required data.
In your case you can do it like below:
$orders = Order::with(['codes' => function($q){
$q->with('events');
})->get();
May be you can get them with nested manner(not sure about this because i didn't tried before posting):
$orders = Order::with('codes.events')->get();
put return $orders; in your controller to see the query.
Enjoy!

Categories