I have two models
model_1
model_2
model_1 has many model_2
Now I want to association model_1 hasMany model_2 match with multiple column.
Let me give an example in raw query
select ...... from model_1 left join model_2 ON (model_1.f1 = model_2.f1 AND model_1.f2 = model_2.f2)
How can I do this in hasMany association
I faced this situation while dealing with a pre existing schema. I came up with this solution
After installing Compoships and configuring it in your models model_1 and model_2, you can define your relationships matching multiple columns.
In model_1:
public function model_2()
{
return $this->hasMany(model_2::class, ['f1', 'f2'], ['f1', 'f2']);
}
In model_2:
public function model_1()
{
return $this->belongsTo(model_1::class, ['f1', 'f2'], ['f1', 'f2']);
}
Compoships supports eager loading.
I got it to work by adding an additional parameter to join on in the model:
public function my_joined_columns($mySecondJoinColumnValue)
{
return $this->hasMany('NamespaceToOtherModel', 'myIdColumn')
->where('my_second_join_column', $mySecondJoinColumnValue);
}
And then I make sure I pass in the parameter:
MyModel1::find(1)->my_joined_columns(2)->get()
Related
I have two tables in MySQL Apoderados and Alumnos each with their primary identifier id, then I have another table Apoderado_Alumno where the id of each table is added, The main idea is that a Proxy can have 1 or more Alumno and that 1 Alumno can belong to One more Apoderado
I currently have the following relationships in models
ApoderadoAlumno
public function apoderado()
{
return $this->hasOne(Apoderado::class,'id','apoderado_id');
}
public function alumno()
{
return $this->hasOne(Alumno::class,'id','alumno_id');
}
Is it the correct way? I feel no, do I have to add the relationship to the Student and Teacher models?
Update
Sorry for the confusion, create the intermediate table because the Alumno may have more than 1 Apoderado (father and mother or other)
After your update.. it means that the relationship between the two models should be a Many to Many relationship:
- Apoderado m ----- m Alumno.
Defining relationships
As you can see in the documentation. Relationships are defined in the model. So in:
- Alumno model, add the method apoderados():
...
public function apoderados()
{
return $this->belongsToMany(Apoderado::class,
'apoderado_alumno',
'alumno_id',
'apoderado_id'
);
}
...
Now in your Apoderado model:
...
public function alumnos()
{
return $this->belongsToMany(Alumno::class,
'apoderado_alumno',
'apoderado_id',
'alumno_id'
}
...
In your controller
Then for querying, for example alumnos related to an apoderado, you just need to do:
$apoderado = Apoderado::find($apoderado_id);
return $apoderado->alumnos; // this will return a collection of alumnos.
I need to get all appeals, that have appeal_stage.expiration_date less than NOW().
Now I have following solution:
public function scopeExpired($query) {
$query->join('appeal_stage', 'appeals.id', 'appeal_stage.appeal_id')
->where('appeal_stage.expiration_date', '<=', new Expression('NOW()'));
}
but resulted model dump shows that joined table is recognized as pivot table:
So, I want to ask - Is there some more convenient way to perform this request?
My suggestions is use Illuminate\Database\Eloquent\Relations\Pivot somehow, bu I do not quiet understand, how Pivot can be used here.
UPD 1
Models has next relations:
public function stages()
{
return $this->belongsToMany(Stage::class)->withPivot('prolongated_count', 'expiration_date')->withTimestamps();
}
public function appeals() {
return $this->belongsToMany(Appeal::class);
}
You should be able to do something like this:
$appeal->stages()->wherePivot('expiration_date', '<', $now)->get()
You should create relationship in appeal model
public function stages()
{
return $this->belongsToMany(Stage::class,'appeal_stage','appeal_id','stage_id')->wherePivot('expiration_date','<',Carbon::now())->withTimestamps();
}
In belongs To Many relationship second argument is your Pivot table name
I'm using Laravel 5.4.22 (the newest one). In MySQL, I have two tables, tag_categories and tags, which form a many-to-many relationship. What I need is a query which returns all the tags for the selected categories. I know how to solve this when I have only one object, and I know how to solve this with querying and looping each of those objects, but there has to be a query or eloquent based solution for the whole thing?
I understand the code below doesn't work because I'm using ->belongsToMany on a collection rather than an object, but how to I bridge this gap the simplest way?
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)
->belongsToMany(Tag::Class)->get();
dd($resultingTags);
belongsToMany generally belongs in the model class, not a method called on the fly. When looking to eager load the relationship, you then call the with() method on the query builder.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
ex:
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
// Query
$users = User::with('roles')->get();
$rolesOfFirstUser = $users->first()->roles;
If you're trying to get all the tags of the given categories, then you should be querying tags, not tag_categories.
Tag::whereHas('categories', function ($query) use ($chosenCategoriesIds) {
$query->whereIn('id', $chosenCategoriesIds);
})->get();
This is One-to-many relation
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->hasMany('App\Tag');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
If you want define Many-To-Many relation for this case
You need to have 3 tables tags, tag_categories, tag_tag_category
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->belongsToMany('App\Tag', 'tag_tag_category', 'tagcategory_id', 'tag_id');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
Is it possible to have a hasMany relationship on two columns?
My table has two columns, user_id and related_user_id.
I want my relation to match either of the columns.
In my model I have
public function userRelations()
{
return $this->hasMany('App\UserRelation');
}
Which runs the query: select * from user_relations where user_relations.user_id in ('17', '18').
The query I need to run is:
select * from user_relations where user_relations.user_id = 17 OR user_relations.related_user_id = 17
EDIT:
I'm using eager loading and I think this will affect how it will have to work.
$cause = Cause::with('donations.user.userRelations')->where('active', '=', 1)->first();
I don't think it's possible to do exactly what you are asking.
I think you should treat them as separate relationships and then create a new method on the model to retrieve a collection of both.
public function userRelations() {
return $this->hasMany('App\UserRelation');
}
public function relatedUserRelations() {
return $this->hasMany('App\UserRelation', 'related_user_id');
}
public function allUserRelations() {
return $this->userRelations->merge($this->relatedUserRelations);
}
This way you still get the benefit of eager loading and relationship caching on the model.
$cause = Cause::with('donations.user.userRelations',
'donations.user.relatedUserRelations')
->where('active', 1)->first();
$userRelations = $cause->donations[0]->user->allUserRelations();
Compoships adds support for multi-columns relationships in Laravel 5's Eloquent.
It allows you to specify relationships using the following syntax:
public function b()
{
return $this->hasMany('B', ['key1', 'key2'], ['key1', 'key2']);
}
where both columns have to match.
I'd prefer doing it this way:
public function userRelations()
{
return UserRelation::where(function($q) {
/**
* #var Builder $q
*/
$q->where('user_id',$this->id)
->orWhere('related_user_id',$this->id);
});
}
public function getUserRelationsAttribute()
{
return $this->userRelations()->get();
}
If anyone landed here like me due to google:
As neither merge() (as suggested above) nor push() (as suggested here) allow eager loading (and other nice relation features), the discussion is still ongoing and was continued in a more recent thread, see here: Laravel Eloquent Inner Join on Self Referencing Table
I proposed a solution there, any further ideas and contributions welcome.
You can handle that things with this smart and easy way .
$cause = Cause::with(['userRelations' => function($q) use($related_user_id) {
$q->where('related_user_id', $related_user_id);
}])->where('active', '=', 1)->first();
I can't select columns in eager loading using Many To Many Polymorphic relations
My request is :
Etape::with(array('entreprises'=> function($query) {
return $query->select('entreprises.id');
}))->get();
The relations are defined as following
class Etape extends Eloquent {
public function entreprises()
{
return $this->morphedByMany('Entreprise', 'etapeable')->withPivot('date');
}
}
class Entreprise extends Eloquent {
public function etapes() {
return $this->morphToMany('Etape', 'etapeable')->withPivot('date')->orderBy('date');
}
}
These SQL requests are executed :
SELECT * FROM `etapes` WHERE `etapes`.`deleted_at` IS NULL
SELECT `entreprises`.`id`, `entreprises`.*, `etapeables`.`etape_id` as `pivot_etape_id`, `etapeables`.`etapeable_id` as `pivot_etapeable_id`, `etapeables`.`date` as `pivot_date` FROM `entreprises` inner join `etapeables` on `entreprises`.`id` = `etapeables`.`etapeable_id` WHERE `entreprises`.`deleted_at` IS NULL and `etapeables`.`etape_id` in ('12', '13', '14') and `etapeables`.`etapeable_type` = 'Entreprise'
As you can see the second request begins with:
SELECT `entreprises`.`id`, `entreprises`.*
So the select method is not taken into account. Is this a Laravel bug or is there an other solution for this issue?
This isn't supported by Laravel (neither 4.* nor 5.*).
Take a look at the issue created by #edi9999: where Taylor Otwell states:
This is a known issue. We don't allow "selects" on by-many relationships.
There is another issue (created at the end of 2013) where Taylor says
We don't currently support limiting the selects on those relations because if it's not done properly it can break the query. You can remove the columns once the data has been retrieved if you like.