How to add condition in connection tables? - laravel

I have two tables: Users and Images.
So, a user can have some images.
For this relationship I have additional function in model User:
public function images()
{
return $this->hasMany('App\Images', 'idElement', 'id');
}
And in controller I have:
$users = Users::where('id', $id)->with("images")->get();
How can I add additional condition in controller for images table that will be "where images.type = 1"?
Now this tables are connected only by primary keys, but I need to set a new condition yet.

You can filter your images with callback function, try this:
$users = Users::where('id', $id)->with(["images" => function ($query){
$query->where('type', 1);
}])->get();

For something like this, where you want to scope down a subset of images based on their type, you can add another method called something like public function scopedImages() and define it as such:
public function scopedImages() {
return $this->hasMany('App\Images', 'idElement', 'id')->where("images.type", "=", 1);
}
In your controller, you would access this function the same as you would the images() function on User:
$users = Users::where('id', $id)->with(["scopedImages"])->get();
Keep the function images() as well, so if you need to find all images attached to a User, but adding additional functions like this gives you flexibility on what you want to return and when.

Related

Laravel Eloquent is there a better way to write this query?

I have a typical pivot table structure like this:
Users
id [...]
Locations
id [...]
User_Location
id | user_id | location_id
I need to get the locations the current authorized user has access to, and then I need to get all the users who also have access to all of those locations.
I tried to figure out an "eloquent" way to do this, but I'm not familiar enough with it. This works, but I'm wondering if it's the best way to do it?
$locations = auth()->user()->locations(); //returns the user_location records
$locationIds = $locations->pluck('location_id');
$locationUsers = new UserLocation();
$userIds = $locationUsers->whereIn('location_id', $locationIds)->groupBy('user_id')->pluck('user_id');
$users = User::withTrashed()
->whereIn('id', $userIds)
->get();
return view('users.index')->with('users', $users);
here's the locations() relationship referenced in the code:
public function locations()
{
return $this->belongsToMany(Location::class, 'user_location')->withPivot('primary');
}
You must create a new method in the Locations model.
public function users()
{
return $this->belongsToMany(User::class, 'user_location');
}
Then your query could look like this.
$locations = auth()->user()->locations()->with('users')->get();
$users = $locations->pluck('users');
If you need to get all users withTrashed then you should modify the first line for this.
$locations = auth()->user()->locations()->with(['users' => function ($user) {
$user->withTrashed();
}])->get();

How to use custom model function in eloquent

I want to get all user's online friends, how can I call a custom model function inside the eloquent condition?
this is my code
$friends = $user->friends()->where(function (Builder $query){
$query->where('friend', 'yes');
})
->get();
and this is my function in model
public function getIsOnlineAttribute(): bool
{
// check if the user is online or not
return $this->is_online;
}
I can access is_online after eloquent by foreach, but in my case, I want to check everything in one step ( inside where condition in eloquent). how can I do that???
You can't use conditions for eloquent accessors, in this case you can use (assume 1 is database column value):
$friends = $user->friends()->where('is_online', 1)->get();
or
$friends = $user->friends()->whereIsOnline(1)->get();
or you can create eloquent scope on your model:
public function scopeIsOnline($query) {
$query->where('is_online',1);
}
and you can use this eloquent scope on your controller in this way:
$friends = $user->friends()->isOnline()->get();
this worked for me :)
$friends = $user->friends()
->simplePaginate()
->reject(function ($friend) {
return $friend->is_online === false;
});

Best way to query model relationship in Eloquent

I am working within a controller in a Laravel application. I am returning a table to the view. The table is based on my PlanSubmission model. I am receiving parameters through a GET request and using those parameters to return a filtered set of rows to my view.
The first part of my controller looks like this and is working fine:
public function index()
{
//Used for filter. The request is received in the URL
if (request()->has('status')) {
$plans = PlanSubmission::where('status', request('status'))->paginate(25)->appends('status', request('status'));
}
elseif (request('employer_name')) {
$plans = PlanSubmission::where('employer_name', request('employer_name'))->paginate(25)->appends('employer_name', request('employer_name'));
}
I have run into a problem because now I need to use a model relationship in the controller. I am receiving 'advisor_name' from the request. The 'advisor_id" column is the foreign key on the PlanSubmission model. The 'advisor_name' column exists in the Advisor model. I have a function on my PlanSubmission model that looks like this:
public function advisor()
{
return $this->belongsTo(Advisor::class);
}
Initially, I thought there was a way I could do this easily with something like:
$plans = PlanSubmission::where(advisor->name, request('advisor_name'))->paginate(25)->appends('advisor_name', request('advisor_name'));
Of course, this will not work because I cannot enter a relationship into the first parameter in the Where Clause.
I do not know where to go from here. My other thought is to return all the advisors first from the Advisor model like this:
$advisors = Advisor::where('name', request('advisor_name'));
Then, I imagine I would have to somehow loop through that and get the id (primary key) for each of the objects in $advisors and somehow get that into the PlanSubmission where clause. I'm totally lost.
Like Victor mentions in his answer you can use whereHas like so:
PlanSubmission::whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
You didn't asked this directly, but I noticed that you use conditionals to make different queries. Eloquent provides a few way to make this a bit nicer to deal with.
The first which is kind of obvious is that that whatever method you call a builder (query) is returned that you can just add on to. It could be there were some common restrictions in your two cases:
public function index()
{
$query = PlanSubmission::where('something', 42);
if (request()->has('status')) {
$query = $query->where('status', request('status'));
} elseif (..) {
...
}
return $query->paginate(25);
}
Another way to do conditional queries in Laravel is using when. E.g. for status:
$query = $query->when(request->has('status'), function ($query) {
// note that you don't have to return the query
$query->where('status', request('status'));
});
// or PlanSubmission::>when(..)
In your example you cannot both filter by status AND advisor_name, but lets assume that would be okay, then you can combine everything like so:
public function index()
{
return PlanSubmission::query()
//->where('something', 42)
->when(request->has('status'), function ($query) {
$query->where('status', request('status'));
})
->when(request->has('advisor_name'), function ($query) {
$query->whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
})->paginate(25);
}
This approach may seem verbose for simple queries and then it is fine to use if conditions, but for complex queries when can be useful. Also the idea of "building up a query" also works nice in those situation. You can pass the query builder around and continuously build it up.
You can use whereHas for that
docs

How to retrieve multiple relations with multiple tables in laravel eloquent

I'm using Laravel 5.8 to build a babysitting site. I have 4 tables with different relationships as below:
please see this image
The relationships are:
Babysitter->hasMany(session)
Sessions->hasOne(Review)
Sessions->hasOne(Kids)
Sessions->hasOne(Babysitter)
Sessions->hasOne(Parent)
I want to achieve 2 things:
First one
I want to show this result when listing all babysitters. I'm showing this information for each babysitter:
plsease see this image
See here what I couldn't achieve
plsease see this image
This is my code
Sitters::where('Status', 'active')->where('Verified', 1)->get();
Second one
Also, I've tried to show kids name with parent review as shown here:
plsease see this image
This is what i'm using
Sessions::select('Reviews.*', 'Sessions.Parent_id')->join('Reviews', 'Reviews.Session_id', '=', 'Sessions.id')->with('owner')->where('Trainer_id', session('user')->Id)->where('Status', '=', 'complete')->with('owner')->orderBy('Sessions.id', 'DESC')->get();
Here is Session.php Model
public function owner(){
return $this->belongsTo('App\Models\Parents', 'Parent_id');
}
As discussed change the relations:
Babysitter->hasMany(sesstion)
Sessions->hasOne(Review)
Sessions->belongsTo(Kids)
Sessions->belongsTo(Babysitter)
Sessions->belongsTo(Parent)
First one
in Babysitter.php declare the following attributes
class Babysitter extends Model
{
public function reviews()
{
$this->hasManyThrough(Review::class, Session::class);
}
public function getAverageReviewAttribute()
{
return $this->reviews()->avg('Rating');
}
}
Then you just need to call it on the model instance.
$babysitter = Babysitter::first();
return $babysitter->average_review;
Second one
Just use the relation
$babysitter = BabySitter::with(['sessions' => public function ($session) {
$session->with(['review','parent','kids']);
})->where('trainer_id', '=', session('user')->Id) //did not understand this condition
->first();
This assumes you have parent, kids and review relation declared on Session::class. (change the names if needed)
After a few days of searching & testing, this is what worked for me:
Inside (Sitters) Model, put this relation
public function sessions()
{
return $this->hasMany(Sessions::class, 'sitter_id')
->withCount('reviews')
->withCount(['reviews as review_avg' => function($query){
$query->select(DB::raw('AVG(Rating)'));
}]);
}
Also, inside (Sessions) Model, put this relation
public function reviews()
{
return $this->hasOne(Reviews::class, 'Session_id');
}
Now you query like this
return $sitters = Sitters::with('sessions')->get();
I hope this can help someone :)

how to send variable from controller to model function in laravel

I want to send variable $typeid to function categories to use it in query is there a way Knowing that when I try to use new instance of class in my controller like that:
$cat= new Main_category();
$categories = $cat->categories()->get();
it returns empty array
the following code is working well when I manually add the typeid inside the model function I want to have it as a variable sent from controller
controller:
$categories = Main_category::with('categories')->get();
Model
public function categories()//($typeid)
{
$query = $this->hasMany(Category::class, 'main_cat_id')
->join('category_type','category_type.cat_id','=', 'categories.cat_id')
->join('main_categories','main_categories.main_cat_id','=', 'categories.main_cat_id')
->where('category_type.type_id', '1'); // I want to use $typeid here
return $query;
}
I am not sure whether you can pass your variable in eloquent relationship methods by using with method or not. But you can add a where clause in controller.
Main_category::with(['categories' => function($query) use($typeid) {
$query->where('category_type.type_id', $typeid);
}])->get();
Or you can create a query scope for model too.
in Model
public function scopeWithCategories($query, $typeid) {
return $query->with(['categories' => function($query) use($typeid) {
$query->where('category_type.type_id', $typeid);
}]);
}
and finally in Controller
Main_category::withCategories($typeid)->get();

Resources