Doctrine Query Builder - How to select a many-to-many relationship? - doctrine

I have a form in Symfony2 that needs to have an entity choice field filled with a collection of Players in a particular tournament. I create the form Type and pass the tournament ID to the query_builder property of the correct field. In the entity repository I have this method:
public function allPlayersInTournamentQuery($tournament_id)
{
$repo = $this->getEntityManager()->getRepository('GameBundle:Tournament');
$tournament = $repo->find($tournament_id);
$players = $tournament->getPlayers();
$playersIds = array();
foreach ($players as $player) {
$playersIds[] = $player->getId();
}
$playersQuery = $this->createQueryBuilder('p')
->in('p.id', $playersIds)
->orderBy('p.real_name', 'ASC');
return $playersQuery;
}
The function in() does not exist in the query builder. I hope the method shows what I am trying to do. Im tying to return the query builder that selects the correct players found in the given tournament.
How can I achieve this?
Thanks!

You can use the helper methods provided by the query builder $playersQuery->expr()->in('p.id', $playersIds)
Your query will be something like that:
$playersQuery = $this->createQueryBuilder('p');
$playersQuery->where($playersQuery->expr()->in('p.id', $playersIds))
->orderBy('p.real_name', 'ASC');
More information about the helper methods here

Related

many to many and one to many relationship in Eloquent

I have three tables: "courriers" which is connected with "reponses" by one to-many relationship (1 courrier could have many reponses), and "structures" which is connected with "courriers" by many-to-many relationship
I want to find the courriers which are connected to a certain structure and doesn't have a reponse in table "reponses".
For example, for the structures "DMO" that has 1 as identifiant in "structures", I wish find the courriers that belongs to this structure and doesn't appear in "reponses".
Am using Laravel 8, I want to do this with Eloquent ORM.
Am trying this
public function dmoDG()
{
$structure = Structure::find(1);
$cou = $structure->courriers;
$courr = $cou->where('repondre','=',1)-
>where('dmo_sec','<>',NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Method Illuminate\Database\Eloquent\Collection::doesntHave does not exist.
When you use $model->relation, it fetches all related records and returns them as a Collection.
If you want to use query builder on a relation, you need to use it as a method: $model->relation()
So, if you access your relation as a property, you got Collection.
But if you access your relation as a method, you got query builder and add your where clauses on it.
In your example;
public function dmoDG()
{
$structure = Structure::find(1);
// $cou = $structure->courriers; // for using without parentheses you got a collection, not a query builder
$cou = $structure->courriers(); // now you will have a query builder and Where clauses will work on this
$courr = $cou->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Actually you can pipe them to one liner:
public function dmoDG()
{
$structure = Structure::find(1);
$courriers = $structure->courriers()->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL)->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Make sure your relations and column names correctly specified.

Eloquent how to pass parameters to relationship

The code I'm trying to fix looks like this. I have an Hotel class which is used in a query to get all hotels in an area but it doesn't discard those which are not available. There's a method inside which should be an accessor but it's not written the way I expected it to be:
public function isAvailableInRanges($start_date,$end_date){
$days = max(1,floor((strtotime($end_date) - strtotime($start_date)) / DAY_IN_SECONDS));
if($this->default_state)
{
$notAvailableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','0']
])->count('id');
if($notAvailableDates) return false;
}else{
$availableDates = $this->hotelDateClass::query()->where([
['start_date','>=',$start_date],
['end_date','<=',$end_date],
['active','=',1]
])->count('id');
if($availableDates <= $days) return false;
}
// Check Order
$bookingInRanges = $this->bookingClass::getAcceptedBookingQuery($this->id,$this->type)->where([
['end_date','>=',$start_date],
['start_date','<=',$end_date],
])->count('id');
if($bookingInRanges){
return false;
}
return true;
}
I wanted to filter out hotels using this query. So this is the query from the controller:
$list = $model_hotel->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
Is it possible to pass the range of days to the function?
By the way the first thing I tried was to use the collection after the query and pass a filter function through the collection and after that paginate manually but although it does filter, but apparently it loses
the "Eloquent" result set collection properties and it ends up as a regular collection, thus it doesn't work for me that way.
Maybe the best approach for that is to create a query scope (source) and put all your logic inside of this function.
after that you can call this scope and pass the dates. Example you will create a query scope and paste your code inside of it.
public function scopeisAvailableInRanges($query, $start_date, $end_date) {
}
then you will invoke this query scope in your controller like this.
$list = $model_hotel::isavailableinranges($start_date, $end_date)->with(['location','hasWishList','translations','termsByAttributeInListingPage'])->get();
keep in mind that inside of your query scope you will return a collection. A collection of all your available hotels.

How to find a model using pivot table data in Laravel?

i have a tournament and a club model.i use many to many relationship between them.now i want to find a club model using pivot table.
i've tried this:
$tournament = Tournament::find(1);
$club = $tournament->clubs->wherePivot('team_as_1','1');
return $club;
but it shows:Method Illuminate\Database\Eloquent\Collection::wherePivot does not exist.
My tournament model:
public function clubs(){
return $this->belongsToMany('App\Club','tbl_club_tournament')->withPivot('team_as_1','team_as_4','team_as_5','team_as_6');
}
My club model:
public function tournament(){
return $this->belongsToMany('App\Tournament','tbl_club_tournament')->withPivot('team_as_1','team_as_4','team_as_5','team_as_6');
}
i want to find a club where team_as_1 = 1.
Try doing
$tournament = Tournament::find(1);
$club = $tournament->clubs()->wherePivot('team_as_1','1')->get();
return $club;
With the current approach you're calling method wherePivot on a collection (but that method doesn't exist on the collection class), however by calling the function $tournament->clubs(), that returns a query builder object on which you can call wherePivot()
Edit:
Seems you only need one item, so you should probably do
$club = $tournament->clubs()->wherePivot('team_as_1','1')->first();
Use Below
$tournament = Tournament::find(1);
$clubs = $tournament->clubs()->wherePivot('team_as_1','1')->get();
return $clubs;
return $this->hasManyThrough('App\Club', 'App\Tournament');

Laravel / Eloquent: Is it possible to select all child model data without setting a parent?

I have various parent/child relationships, drilling down a few levels. What I want to know is if its possible to do something like this:
$student = Student::find(1);
$student->bursaries()->enrolments()->courses()->where('course','LIKE','%B%');
(With the end goal of selecting the course which is like '%B%'), or if I would have to instead use the DB Query builder with joins?
Models / Relationships
Student:
public function bursaries() {
return $this->hasMany('App\StudentBursary');
}
StudentBursary:
public function enrolments() {
return $this->hasMany('App\StudentBursaryEnrolment');
}
If what you want is to query all courses, from all enrollments, from all bursaries, from a students, then, unfortunately, you are one table too many from getting by with the Has Many Through relationship, because it supports only 3 tables.
Online, you'll find packages that you can import / or answers that you can follow to provide you more though of solutions, for example:
1) How to use Laravel's hasManyThrough across 4 tables
2) https://github.com/staudenmeir/eloquent-has-many-deep
Anyhow, bellow's something you can do to achieve that with Laravel alone:
// Eager loads bursaries, enrolments and courses, but, condition only courses.
$student = Student::with(['bursaries.enrolments.courses' => function($query) {
$query->where('course','LIKE','%B%');
}])->find(1);
$enrolments = collect();
foreach($student->bursaries as $bursary) {
$enrolments = $enrolments->merge($bursary->enrolments);
}
$courses = collect();
foreach ($enrolments as $enrolment) {
$courses = $courses->merge($enrolment->courses);
}
When you do $student->bursaries() instead of $student->bursaries, it returns a query builder instead of relationship map. So to go to enrolments() from bursaries() you need to do a bursaries()->get(). It should look like this.
$student->bursaries()->get()[0]->enrolments(), added the [0] because im using get(), you can use first() to avoid the [0]
$student->bursaries()->first()->enrolments()
But I'm not sure if it will suffice your requirement or not.

Change collection to query builder in laravel

I have a function with return type Builder
For some specific case, i need to change the value of collection (which i'll get from builder object) and return updated value as builder object.
Is there any way to convert a collection back to it's builder object?
Here is what i want to achieve in simpler form:
$users = DB::table('users')->where('is_human',1); // Builder obj
$isAlien = true;
$users = $users->get()->map(function($user) use ($isAlien) {
$user->is_human = $isAlien? 0 : 1;
return $user;
});
Now, the problem is when i execute this code, it will return a collection.
But i want to return $users as Builder Object.
I have searched through the laravel's API document to find a solution.
Please share your knowledge.

Resources