use relations in scopes in laravel - php

I Want To Use Relation Methods In Scopes But It Gives an Error.
Error:
Call to undefined method Illuminate\Database\Eloquent\Builder::members()
Controller:
$members = $book->MembersLoanedCurrentBook()->paginate(8);
Scope:
public function scopeMembersLoanedCurrentBook(Builder $query): Builder
{
return $query->members()->orderBy('return_date')->where('book_member.returned',false);
}

Assuming your models are something akin to User hasMany BookMember and Book has an attribute called returned, you can use Laravel's with` query scope:
Users::with(['member_books', function ($q) => {
$q->returned
})->get();
#geertjanknapen was right that this is a possible duplicate. You can achieve the same result using the methods from this question.
What you are doing is defining a scope and in that scope querying a relationship for a specific property or value.
public function scopeMembersLoanedCurrentBook(Builder $query): Builder
{
return $query->members()
->orderBy('return_date')
->whereHas(['book', function ($q) => {
$q->returned == false;
});
});
}
Without knowing the model structure and relationships, it's hard to write out an exact solution, but something along these lines should work.

You can't work with relations in scope, because your work with Builder $query.
public function scopeMembersLoanedCurrentBook(Builder $query): Builder
{
return $query->orderBy('return_date')
->where('returned',false);
}
And
$members = $book->members()->MembersLoanedCurrentBook()->paginate(8);

Related

How to set additional query to recursive relation in Laravel

I need to make the orderBy parameter dynamic (not "name" as it is now)
public function children()
{
return $this->hasMany(self::class,'parent_id','id')
->orderBy('name')
->with(['children' => fn($q) => $q->orderBy('name')]);
}
The best way for me is when you call the relationship on the method with() and add the function as you did on the model, then you be able to pass the parameter on the fly instead since you defined the relationship.
e.g.
Relationship on model
public function children()
{
return $this->hasMany(self::class,'parent_id','id');
}
Call the relationship
$data = Model::with('children' => function($query) use($parameter) {
$query->orderBy($parameter);
});
Try with this solution, I'm not sure if that works, but I did something similar to apply a where conditional on the relationship

Laravel Eloquent get model by scope on associated model

I have an Eloquent model Foo which has a field bar_id. I define the relationship between them in the Foo model:
public function Bar()
{
$this->belongsTo('App\Bar');
}
The Bar model has a baz_id, and a scope to get all Bars which have a particular baz_id. This is the scope in my Bar model:
public function scopeFromBaz($query, $bazId)
{
return $query->where('baz_id', $bazId)
}
I now want to make a call of all Foos where their associated Bar has a baz_id of 1. How do I do this? I've tried:
Foo::where('bar', function($query) {
$query->fromBaz(1);
});
But that produces the error
Call to undefined method Illuminate\Database\Query\Builder::fromBaz()
You have to use whereHas when you're adding conditions to relationships.
The query should be
Foo::whereHas('Bar', function ($query) {
$query->fromBaz(1);
})->get();
See : https://laravel.com/docs/5.6/eloquent-relationships#querying-relationship-existence
I also spotted something on your other piece of code, you haven't added a return on your Bar function in the Foo model.

Pass variable to laravel method from within the same model

I am working on some multi tenancy updates to a Laravel app but hitting an issue when trying to pass a specific team ID into a method on a model from within another method.
Example:
In Controller:
$waitTime = $booking->estimatedWaitTime($teamId);
In Booking model:
public function queueLength()
{
$booking = $this->todaysBookings;
foreach ($bookings as $booking) {
// Calculate the length of all bookings
}
}
public function todaysBookings()
{
return $this->hasMany('App\UserBooking')->whereHas('booking', function ($q) {
$q->where('team_id', 2);
});
}
This correctly returns the bookings and allows me to loop through them in the queueLength method. However, I want to be able to pass the team_id into the todaysBooking method.
When instead calling todaysBookings as a method:
$booking = $this->todaysBookings();
It doesn't return anything for me to loop through.
Any ideas how to achieve what I want to do here?
You can pass the id as parameter and then get the results :
public function todaysBookings($team_id)
{
return $this->hasMany('App\UserBooking')->whereHas('booking', function ($q) use($team_id) {
$q->where('team_id', $team_id);
});
}
In the call you can do as follow :
$some_teame_id = 2;
$booking = $this->todaysBookings($some_teame_id)->get();
Why ?
Because the relationships in laravel serve as powerful query builders (documentation) :
Eloquent relationships are defined as methods on your Eloquent model
classes. Since, like Eloquent models themselves, relationships also
serve as powerful query builders, defining relationships as methods
provides powerful method chaining and querying capabilities.
Relationship Methods Vs. Dynamic Properties :
The call of the relationship as Methods is needed when you want to add some more conditions and filters like this :
$booking->todaysBookings()->where('active', 1)->get(); //just an example :)
And if you do not need to add additional constraints to an Eloquent relationship query, you may simply access the relationship as if it were a property, Laravel will add the get() for you :)
$booking->todaysBookings

laravel pulling data from database

I'm still trying to wrap my head around whereHas() method. My case is this. I want to pull all users that belong to class
This is relations
Classes model
public function users() {
return $this->belongsToMany('App\User')->withTimestamps();
}
User model
public function classes() {
return $this->belongsToMany('App\Classes')->withTimestamps();
}
controller
$class_us = User::whereHas('classes', function ($query) {
$query->where('class',1);
})->get();
When I do dd($class_us) I get an empty collection.
How can I resolve this problem?
Thanks.
in whereHas clouse the builder instance is coming from the related model so,
$class_us = User::whereHas('classes', function ($query) {
// here the builder belongs to Class model not User model
$query->where('id',1); // id because classes table has column as id
})->get();

Eloquent relation with custom foreign - other key value

Is there a way to create an Eloquent relation function based on custom foreign - other key value?
For example I have level relation:
public function level(){
return $this->belongsTo(Level::class, 'level_number', 'number');
}
And I want to do something like this:
public function nextLevel(){
return $this->belongsTo(Level::class)->where('number', '=', $this->level_number + 1);
}
Is this possible or I have to write a raw query?
I had a similar situation, so I did some research in the Laravel base code and I found a very nice and clean solution.
Laravel (5.8) in BelongsTo class uses something like this finally:
$this->query->where($table.'.'.$this->ownerKey, '=', $this->child->{$this->foreignKey});
$this->child is a variable which holds Eloquent object, so we can use the magic of Eloquent getters.
The solution is creating a getter with any name and using it as a second argument, as a foreign key in relations declaration:
public function level()
{
return $this->belongsTo(Level::class);
}
public function nextLevel()
{
return $this->belongsTo(Level::class, 'next_level_id');
}
public function getNextLevelIdAttribute()
{
// any logic to get id (static value, db query etc..)
return $this->id + 1;
}

Categories