Laravel Join Using Eloquent Relations - php

I have four models created, and their relations have been defined correctly. What I haven't grasped is how to use those relations for my situation.
Let's use countries > regions > cities > censuses as a contrived example of the relations. I can get the most recent date a census was taken on with this function in the Census model:
public static function getMaxTakenOn($country_id)
{
return \DB::table('regions')
->join('cities', 'cities.region_id', '=', 'region.id')
->join('censuses', 'censuses.city_id', '=', 'cities.id')
->where('regions.country_id', '=', $country_id)
->max('censuses.taken_on');
}
How would I rewrite the function body to use the Eloquent Relations?

Using the hasMany and a custom field in each of the models:
https://laravel.com/docs/5.5/eloquent-relationships
Example in your Regions model, you will need a:
public function cities() {
return $this->hasMany('App\Cities');
}
Then so on in your other models.
Once that is done, you don't need the 2 joins, you can access them like:
$region = new Region();
$region->where('country_id', '=', $country_id)
->max('censuses.taken_on');
echo $region->cities()->censuses()->count();

Related

Laravel Multiple relationships data fetch

Good day. I have this relationship problem in laravel
I have three models
User
State
School
I have established the following relationships between them
a. School - User (A user can enroll in many schools)
In User Model
public function schools(){
return $this->belongsToMany('App\Models\School', 'school_user', 'user_id', 'school_id');
}
b. A school can have many users
In School Model
public function users(){
return $this->belongsToMany('App\Models\User', 'school_user', 'school_id', 'user_id');
}
c. A school belongs to a state (i.e c an be found in a state)
In School Model
public function states(){
return $this->belongsTo('App\Models\State');
}
d. A state has many schools
In State Model
public function schools(){
return $this->hasMany('App\Models\School');
}
Now, I know how to query models with immediate relationship. For example, I can get the users associated with a school and vice-versa. I can also get the schools in a state.
My Question
How do I get all users associated with schools in a given state (using a query)? Assuming all we have is just the name of a state.
And probably get all schools the users are associated with and the date(time) in which there were associated with the schools?
This is what I have done. The second query is not giving me an answer
public function details(){
//Get all schools associated with a state
$schools = State::with('schools')->where('id',1)->first();
foreach($schools->schools as $data){
//Get all users associated with the schools
$users = School::with('users')->where('id',$data->id);
dd($users);
}
}
Apart from the fact that this approach is not probably right, I am not getting any answer. Is there a query or method that can solve this?
Thanks.
You should see the related models in the relations array attribute
$school= App\Models\School::with('User', 'State')->get();
Using a whereHas you can get all users that belong to schools with the given state_id.
$state = State::find(1);
$users = User::whereHas('schools', function ($query) use ($state) {
$query->where('state_id', $state->id);
})->get();
Querying Relationship Existence
If you need even more power, you may use the whereHas and orWhereHas
methods to define additional query constraints on your has queries,
such as inspecting the content of a comment:
use Illuminate\Database\Eloquent\Builder;
// Retrieve posts with at least one comment containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
})->get();
// Retrieve posts with at least ten comments containing words like code%...
$posts = Post::whereHas('comments', function (Builder $query) {
$query->where('content', 'like', 'code%');
}, '>=', 10)->get();

Return belonging name with ID form Laravel, check for the type?

sorry for the title of this question but I am not sure how to ask it...
I am working on a project where I have two Models Trains and Cars, to this model I have a belonging Route.
I want to make a query and check if the routeable_type is App\Car than with the selected routeable_id to get the data from the Car. And if the routeable_type is Train then with the ID to get the data from the Tran.
So my models go like this:
Train:
class Train extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Car:
class Car extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Route:
class Route extends Model
{
public function routeable()
{
return $this->morphTo();
}
}
And the query I have at the moment is:
$data = Route::leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')
->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')
->select('routes.id', 'cars.model AS carmodel', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
With this query if I have the same ID in cars and trains I get the data from both and all messes up. How do I check if routeable_type is Car ... do this, if routeable_type is Train .. do that?
Will something like this be possible in a 1 single query:
$data = Route::select('routes.id', 'routeable_type', 'routes.created_at');
if(routeable_type == 'Car'){
$data = $data->leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')->select('routes.id', 'cars.model AS carmodel', 'routeable_type', 'routes.created_at');
}else{
$data = $data->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')->select('routes.id', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
}
Maybe this is what you are looking for?
DB::table('routes')
->leftJoin('cars', function ($join) {
$join->on('cars.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Car');
})
->leftJoin('trains', function ($join) {
$join->on('trains.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Train');
})
->select('routes.id', 'cars.model AS car_model', 'trains.model AS train_model', 'routes.routeable_type', 'routes.created_at');
->get();
I think you may want to follow the morphedByMany design.
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many-polymorphic-relations
This was also a neat visual for the different relation types.
https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209
I was faced with a similar issue though I failed to follow the correct design initially and was forced to query the many possible relations then wrote custom logic after to collect the relation types and ids then do another query and assign them back through iteration. It was ugly but worked... very similar to how Eloquent does things normally.
i don't have enough repo, so i can't comment. that's why i am putting as an answer.
You should use 2 different queries, for each model.
This will be better, code wise as well as performance wise. also if both models have similar fields you should merge them to 1 table and add a 'type' column.
and put non-similar fields in a 'meta' column.
( in my opinion )

Laravel querying multiple related models

For example: I have these models in my application.
User, Profile, Interest.
I linked the users table with the profiles table by adding the user_id column in the profiles table. And I linked profiles and interests by using a pivot table (interest_profile), Which is (as obvious) will have two columns (profile_id, interest_id).
However, I want to query the users who are associated with a profile, too see who is associated with a particular interest, In other words: "select all users who are having (in their profiles) that particular interest".
I know that I can do this with raw SQL by joining the four tables and then use (where clause).. But I want to do it the Laravel way.
Thanks in advance.
First make sure you have your relationships setup correctly on your models like:
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
class Profile extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function interests()
{
return $this->belongsToMany(Interest::class, 'interest_profile');
}
}
class Interest extends Model
{
public function profiles()
{
return $this->belongsToMany(Profile::class, 'interest_profile');
}
}
Then you can use whereHas() to constrain a query by a related model and dot notation for nested relations. So your query would be:
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})->get();
That would just return a collection of users. If you wanted to return their profiles and interests as well you would use with():
User::whereHas('profile.interests', function($query) use ($interestName) {
return $query->where('name', $interestName);
})
->with('profile.interests')
->get();
Assuming the User model has a relationship profile and the Profile model has a relationship interests, you can do this.
$interest_id = 1;
$users = User::whereHas('profile', function ($query) use ($interest_id) {
$query->whereHas('interests', function ($query) use ($interest_id) {
$query->where('id', $interest_id);
});
})->get();

php/laravel - select other table's column using the belongsToMany of laravel

This is my code on patient model:
class Patient extends Model
{
protected $primaryKey = 'PatientID';
public function users()
{
return $this->belongsToMany('App\Vaccine', 'immunizations', 'patient_id', 'vaccine_id');
}
}
and this is my query
$patients = Patient::whereDoesntHave('users', function ($q) use ($vaccine_id) {
$q->where('vaccine_id', '=', $vaccine_id);
})->get();
In my current situation I can only get the Patient model column data but not the other tables. Where should I put the select() method to select the rows and columns of the immunizations table because i want to set a chain where()or condition which is where('immunizations_id', 1) but it doesn't work because the immunizations table is not selected.
or does anyone here knows how to convert it without using a closure like make it
Patient::wheredoesnthave(join('table')); so i can freely manipulate it
We will get other relationship table using 'with()'
$patients = Patient::whereDoesntHave('users', function ($q) use ($vaccine_id) {
$q->where('vaccine_id', '=', $vaccine_id);
$q->with('Vaccine', 'immunizations');
})->get();
Try this.

Laravel 5: Eloquent - order by relation when returning single record

im trying build query in eloquent with data sorted by relation. Imagine this DB structure:
TABLE: Station
id
name
...
TABLE: station_status:
id
station_id
status_type_id
date
...
TABLE: status_type:
id
description
...
MODELS
class Station extends \Eloquent
{
public function stationStatus() {
return $this->hasMany('App\StationStatus', 'station_id', 'id');
}
}
class StationStatus extends \Eloquent
{
public function statusType() {
return $this->hasOne('App\StatusType', 'id', 'status_type_id');
}
}
class StatusType extends \Eloquent
{
...
}
Now the question. How can i query Station model by station ID, but sort by related status types description?
So far i have:
// This just do not work
$query = Station::join('station_status', 'station.id', '=', 'station_status.station_id')
->join('status_type', 'station_status.status_type_id', '=', 'status_type.id')
->orderBy('status_type.description', 'ASC')
->select(['stations.*'])
->with(['stationStatus.statusType']
->find(110);
I think the problem is that i'm not returning collection but only one item using find() method, how can i overcome this problem?
Many thanks for any help !
Try this:
$query = Station::with(['stationStatus' => function($q){
$q->join('status_type', 'station_status.status_type_id', '=', 'status_type.id')
->orderBy('status_type.description', 'ASC');
}])->find(110);
With is a convienent way of getting the related objects, but it doesn't perform a join. It performs a new query and attaches all the elements to your collection. You can add your own logic to the query, like I did with $q->orderBy(...).

Categories