Laravel hasManyThrough, with belongs to between the other two models - laravel-5

Not sure how clear is the question title, I will try to elaborate:
Tables:
repairs (Model: Repair):
id
id_supplier
id_store
suppliers (Model: Supplier):
id
store (Model: Store):
id
Relations:
Repair:
public function supplier() {
return $this->belongsTo('App\Supplier', 'id_supplier');
}
Supplier:
public function repairs() {
return $this->hasMany('App\Repair', 'id_supplier');
}
Store:
public function repairs() {
return $this->hasMany('App\Repair', 'id_store');
}
What i need from here is to get the collection of all suppliers a given store worked with, through the repairs that were made there, so my first thought it was hasManyThrough, but no ideia what to pass as 4ยบ parameter:
Store:
public function suppliers() {
return $this->hasManyThrough('App\Supplier', 'App\Repair', 'id_supplier', '<NO IDEIA WHAT'S HERE>');
}
What am I missing? Is this the right way?

I think you can't use HasManyThrough here. Try something like this in your code:
$store = Store::with('repairs.supplier')->find($id_store);//eager load
$repairs = $store->repairs->pluck('supplier');//get array of suppliers id
$suppliers = (new Collection($repairs))->unique();//get unique collection of suppliers (without duplicates)
where $id_store is id of store which suppliers you want to get

Related

Laravel has one through not returning correctly. always returning the first ID

I have a table that looks like this
ContractorDocuments Documents Documents_Category
user_id id id
documents_id documents__category_id name
I tried the following in my ContractorDocuments Model however it is always returning the first Document Category only.
public function Documents_Category() {
return $this->hasOneThrough(Documents_Category::class, Documents::class, 'documents__category_id', 'id', 'documents_id','documents__category_id');
}
documents__category_id == foreign key on Documents Table
id = foreign key on owners table(Documents_Category)
documents_id = local key on owners table(ContractorDocuments)
documents__category_id = local key on Documents table
Would appreciate any help. Thank you!
From ContractorDocuments to Documents_Category, you don't need hasOneThrough relationship since ContractorDocuments can only have one Documents_Category through belongsTo().
// ContractorDocuments model
public function documents()
{
return $this->belongsTo(Documents::class, 'documents_id');
}
And
// Documents model
public function documents_category()
{
return $this->belongsTo(Documents_Category::class, 'documents__category_id');
}
You can just call
$contractorDocuments->documents->documents_category;

Get data through pivot table in Laravel

I got 3 tables. Table 1 & 2 has their ids as foreign keys in third one(pivot).
Relations for first one is
$this->hasMany("App\Pivot","game_id");
, second is
$this->belongsToMany("App\Pivot","army_id");
and pivot has relationships with both of them i.e belongsTo.
My schema:
I tried accessing it in controller of first one like this:
$games= Game::with("armies")->get();
Result that i get is array of games where instead of individual army data , i get collection from pivot table.
I can loop through it and get it that way, is there more elegant way of doing it?
If you are using pivot table this is the way how to do it.
Games Model
public function armies()
{
return $this->belongsToMany(App\Armies::class, 'pivot_table', 'game_id', 'army_id');
}
Armies Model
public function armies()
{
return $this->belongsToMany(App\Games::class, 'pivot_table', 'army_id', 'game_id');
}
Access the relationship like this..
Controller
App\Games::first()->armies()->get();
or
App\Games::first()->armies
or
App\Games::find(1)->armies
If you're going to use an intermediate table like that I'd probably do something like this:
Games model
public function armies()
{
return $this->belongsToMany('App\Armies');
}
Armies model
public function games()
{
return $this->belongsToMany('App\Games');
}
I'd keep the table structures all the same but rename the "pivot" table to armies_games since this is what Laravel will look for by default. If you want to keep it named Pivots, you'll need to pass it in as the second argument in belongsToMany.
With this, you don't really need the Pivot model, you should just be able to do:
$armies = Game::first()->armies()->get();
or
$armies = Game::find(3)->armies()->orderBy('name')->get();
or
$game = Game::first();
foreach ($game->armies as $army) {
//
}
etc.

Laravel - Eloquent relation whereHas one or more other relations

I'm learning Laravel and Laravel eloquent at the moment and now I try to solve a problem using relations in Laravel.
This is what I want to archive:
The database holds many sport clubs. A sport club has a lot of teams. Each team has games. The teams table has a column named club_id. Now I want to create Eloquent relations to get all games of a club.
Here is what I got so far:
Club model
id => PRIMARY
public function games()
{
return $this->hasMany('App\Models\Games')->whereHas('homeTeam')->orWhereHas('guestTeam');
}
Game model
home_id => FOREIGN KEY of team ; guest_id => FOREIGN KEY of team
public function homeTeam()
{
return $this->belongsTo('App\Models\Team','home_id')->where('club_id','=', $club_id);
}
public function guestTeam()
{
return $this->belongsTo('App\Models\Team','guest_id')->where('club_id','=', $club_id);
}
Team model
id => PRIMARY ; club_id => FOREIGN
In my controller all I want to do is Club::findOrFail($id)->games()
Executing the code above returns a SQL error that the games table does not have a column named club_id.
What is the correct way to create this kind of relation?
Thanks!
EDIT
Thanks to Nikola Gavric I've found a way to get all Games - but only where club teams are the home or away team.
Here is the relation:
public function games()
{
return $this->hasManyThrough('App\Models\Game','App\Models\Team','club_id','home_id');
}
How is it possible to get the games where the home_id OR the guest_id matches a team of the club? The last parameter in this function does not allow an array.
There is method to retrieve a "distant relationship with an intermediary" and it is called Has Many Through.
There is also a concrete example on how to use it which includes Post, Country and User, but I think it will be sufficient to give you a hint on how to create games relationship inside of a Club model. Here is a link, but when you open it, search for hasManyThrough keyword and you will see an example.
P.S: With right keys naming you could achieve it with:
public function games()
{
return $this->hasManyThrough('App\Models\Games', 'App\Models\Teams');
}
EDIT #01
Since you have 2 types of teams, you can create 2 different relationships where each relationship will get you one of the type you need. Like this:
public function gamesAsHome()
{
return $this
->hasManyThrough('App\Models\Games', 'App\Models\Teams', 'club_id', 'home_id');
}
public function gamesAsGuests()
{
return $this
->hasManyThrough('App\Models\Games', 'App\Models\Teams', 'club_id', 'guest_id');
}
EDIT #02
Merging Relationships: To merge these 2 relationships, you can use merge() method on the Collection instance, what it will do is, it will append all the records from second collection into the first one.
$gamesHome = $model->gamesAsHome;
$gamesGuests = $model->gamesAsGuests;
$games = $gamesHome->merge($gamesGuests);
return $games->unique()->all();
Thanks to #HCK for pointing out that you might have duplicates after the merge and that unique() is required to get the unique games after the merge.
EDIT #03
sortBy also offers a callable instead of a attribute name in cases where Collection contains numerical indexing. You can sort your Collection like this:
$merged->sortBy(function($game, $key) {
return $game->created_at;
});
When you define that Club hasMany games you are indicating that game has a foreign key called club_id pointing to Club. belongsTo is the same but in the other way. These need to be coherent with what you have on your database, that means that you need to have defined those keys as foreign keys on your tables.
Try this...
Club model
public function games()
{
return $this->hasMany('App\Models\Games');
}
Game model
public function homeTeam()
{
return $this->belongsTo('App\Models\Team','home_id');
}
public function guestTeam()
{
return $this->belongsTo('App\Models\Team','guest_id');
}
Your Query like
Club::where('id',$id)->has('games.guestTeam')->get();

How to retrieve data through model?

I have Order model with another relation OrderPhoto:
public function OrderPhoto()
{
return $this->hasMany('App\OrderPhoto');
}
In turn OrderPhoto model has relation:
public function Photo()
{
return $this->belongsToMany('App\Photo');
}
So, how to get data from OrderModel with related data from third model Photo?
I guess this:
Order::with("OrderPhoto.Photo")->get();
to retrieve only data from Photo model for each Order
So, each Order has some OrderPhotos. Relationship is one to many.
But one item from OrderPhotos is related with primary key from table Photos. It is one to one relation.
My result query should be:
select `photos`.*, `ordersphoto`.`Orders_Id` from `photos` inner join `ordersphoto` on `ordersphoto`.`Photos_Id` = `photos`.`Id` where `ordersphoto`.`Orders_Id` in (1);
How to use hasManyThrough for this query?
Just having a quick look at your relationships it looks like you could create a hasManyThrough relationship on the order Model.
public function Photo {
return $this->hasManyThrough('App\OrderPhoto', 'App\Photo')
}
You may need to add the table keys to make it work
This will allow you to do:
Order::with("Photo")->get();
You can see more details here https://laravel.com/docs/5.5/eloquent-relationships#has-many-through
Update
Try this
public function Photo {
return $this->hasManyThrough('App\Photo', 'App\OrderPhoto', 'Order_id', 'Photos_id', 'id', 'id')
}
It is a little hard to get my head around your DB structure with this info but you should hopefully be able to work it out. This may also help
https://laravel.com/api/5.7/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_hasManyThrough

How to setup conditional relationship on Eloquent

I have this (simplified) table structure:
users
- id
- type (institutions or agents)
institutions_profile
- id
- user_id
- name
agents_profile
- id
- user_id
- name
And I need to create a profile relationship on the Users model, but the following doesn't work:
class User extends Model
{
public function profile()
{
if ($this->$type === 'agents')
return $this->hasOne('AgentProfile');
else
return $this->hasOne('InstitutionProfile');
}
}
How could I achieve something like that?
Lets take a different approach in solving your problem. First lets setup relationship for the various models respectively.
class User extends Model
{
public function agentProfile()
{
return $this->hasOne(AgentProfile::class);
}
public function institutionProfile()
{
return $this->hasOne(InstitutionProfile::class);
}
public function schoolProfile()
{
return $this->hasOne(SchoolProfile::class);
}
public function academyProfile()
{
return $this->hasOne(AcademyProfile::class);
}
// create scope to select the profile that you want
// you can even pass the type as a second argument to the
// scope if you want
public function scopeProfile($query)
{
return $query
->when($this->type === 'agents',function($q){
return $q->with('agentProfile');
})
->when($this->type === 'school',function($q){
return $q->with('schoolProfile');
})
->when($this->type === 'academy',function($q){
return $q->with('academyProfile');
},function($q){
return $q->with('institutionProfile');
});
}
}
Now you can access your profile like this
User::profile()->first();
This should give you the right profile. Hope it helps.
you can do this by use another method please check this:
a blog Post and Video model could share a polymorphic relation to a
Tag model. Using a many-to-many polymorphic relation allows you to
have a single list of unique tags that are shared across blog posts
and videos. First, let's examine the table structure:
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many-polymorphic-relations
Looks like that should be $this->type rather than $this->$type - since type is a property, not a variable.

Resources