Laravel querying multiple related models - php

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();

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();

Laravel 5.6.8 Eloquent and many to many + many to one joins

I have many to many connect with between user - cityarea.
I have also area which connect cityarea (One cityarea can connect only one area).
I have this database structure:
users
id
username
password
cityareas
id
name
area_id
cityarea_user
id
cityarea_id
user_id
areas
id
name
Next I have Models
User
public function cityareas()
{
return $this->belongsToMany('App\Cityarea');
}
Cityarea
public function area()
{
return $this->belongsTo('App\Area');
}
public function users()
{
return $this->belongsToMany('\App\User');
}
Area
public function cityareas()
{
return $this->hasMany('App\Cityarea');
}
QUESTION:
How I can get all users where areas.name = "South" with Eloquent ?
Thanks!!
By using whereHas, you can do:
$users = User::whereHas('cityareas.area', function ($query) {
$query->where('name', 'South');
})->get();
Jeune Guerrier solution is perfect, but you can use with() method of eloquent If you also need cityarea collection along with users collection.
$users = User::with('cityareas')->whereHas('cityareas.area', function ($query) {
$query->where('name', 'South');
})->get();
This is exactly what the belongs to many relationships is built for.
You simply have to do, Cityarea::where('name', 'South')->first()->users;
If you want to do something further with the query, e.g. sort by users created at, you can do
Cityarea::where('name', 'South')->first()->users()->orderBy('creaated_at', desc')->get();
Note that if there is no such Cityarea with name 'South', the ->first() query above will return null and therefore will fail to fetch the users.
A more performant way to do it programmatically is to use the whereHas approach as discussed in the comments below.

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.

How to get all posts from the User's subscriptions

In my application, I have setup a User model that can have subscribers and subscriptions through a pivot table called subscriptions.
public function subscribers()
{
return $this->belongsToMany('Forum\User', 'subscriptions', 'subscription_id', 'subscriber_id');
}
public function subscriptions()
{
return $this->belongsToMany('Forum\User', 'subscriptions', 'subscriber_id', 'subscription_id');
}
My question is, what relationship should I use to get a list of paginated Post models (belong to a User) from the User's subscriptions?
You can use the whereHas method to filter based on relationships. Assuming your Post model has a user relationship defined, your code would look something like:
// target user
$user = \App\User::first();
$userId = $user->id;
// get all of the posts that belong to users that have your target user as a subscriber
\App\Post::whereHas('user.subscribers', function ($query) use ($userId) {
return $query->where('id', $userId);
})->paginate(10);
You can read more about querying relationship existence in the documentation.
You can do something like this
\App\Post::with(['subscriptions' => function ($query) {
$query->where('date', 'like', '%date%');
}])->paginate(15);
Or without any conditions
\App\Post::with('subscriptions')->paginate(15);

Laravel With and wherePivot

I'm trying to extract all companies and contacts with pivot.main_contact = 1.
Tables:
Company: id, name
Company_contacts: id, company_id, contact_id, main_contact
Contacts: id, name
Model:
class Company extends Model
{
public function mainContact()
{
return $this->belongsToMany('App\Contact', 'company_contacts')
->wherePivot('main_contact', '=', 1);
}
}
Controller:
$query = Company::with('mainContact')->get();
This returns companies + ALL contacts for the companies and NOT ONLY the ones with main_contact = 1.
First, for reasons I'm unsure of, you need to add withPivot('main_contact'); to your relation. This will return main_contact in your collection under pivot
class Company extends Model
{
public function mainContact()
{
return $this->belongsToMany('App\Contact', 'company_contacts')
->withPivot('main_contact');
}
}
The second thing you need to do is use withPivot() while constraint eager loading like so:
$companies = Company::with(['mainContact'=> function($query){
$query->wherePivot('main_contact', 1);
}])->get();
I've checked it, it works.
Just to go a bit above and beyond. Sometimes you'll want to query a pivot table without knowing the value. You can do so by:
$companies = Company::with(['mainContact'=> function($query) use ($contact){
$query->wherePivot('main_contact', $contact);
}])->get();
Try to add withPivot():
return $this->belongsToMany('App\Contact', 'company_contacts')
->withPivot('main_contact')
->wherePivot('main_contact', '=', 1);
Company::with('mainContact')->get(); returns all compaines and all contacts. You should be doing $company->mainContacts()->get(); instead.
$companies=Company::all();
foreach($companies as $company)
{
print_r($company->mainContact()->get());
}
die;

Categories