How to set additional query to recursive relation in Laravel - php

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

Related

use relations in scopes in laravel

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

Return specific items with a relationship

I created a relationship in my 'Event' model called 'Type'.
public function type()
{
return $this->belongsTo('Types');
}
In my 'Types' database, I have several elements. My relationship returns them all, and I would like to know if it was not possible, directly in the relation, to indicate that I only want the types with the column "parent_id" 1.
Thank you very much
If I understand you correctly, you want to add a where clause to your relationship. You can do this right in the definition. I guess this isn't very clear in the laravel 5.5 docs.
Another note, if your model is called "Type" as you stated in the question, then your relationship should use this name as well:
public function type()
{
return $this->belongsTo('App\Type')->where('types.parent_id', 1);
}
... or with a namespace...
use App\Type;
...
public function type()
{
return $this->belongsTo(Type::class)->where('types.parent_id', 1);
}

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

Custom model method to get a relation in Laravel

I try to define a custom Model method in Laravel. I have a n:m relation between Subscription and Notification over SubscriptionNotification.
I already defined the default relations:
public function subscription_notifications() {
return $this->hasMany('App\SubscriptionNotification');
}
public function notifications() {
return $this->belongsToMany('App\Notification', 'subscription_notifications');
}
Now I want to define a method, which returns a collection of notifications. I collect the IDs of the notifications I want in an array and write the following method:
public function notifications_due() {
// Collect $notification_ids
return $this->belongsToMany('App\Notification', 'subscription_notifications')->whereIn('notifications.id', $notification_ids)->get();
}
But when I want to use the mothod by $subscription->notifications_due, I get the following error:
[LogicException]
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
I'm new to Laravel (I come from Rails). I don't know if this is in Laravel even possible. Maybe someone can help me. Thanks!
Remove the ->get() part in the method notifications_due. get() will return a Collection, but when calling the method as a property (or magic method), Laravel expects the method to return an instance of Relation. Laravel will then execute the query and transform it to a Collection automatically.
Also, you can use your already defined notifications() method:
public function notifications_due() {
// Collect $notification_ids
return $this->notifications()->whereIn('id', $notification_ids);
}
Remove the get call from your relationship method, for example:
public function notifications_due() {
return $this->belongsToMany(
'App\Notification',
'subscription_notifications
')->whereIn('notifications.id', $notification_ids);
}
Use it just same:
// It'll return a collection
$dues = $subscription->notifications_due;
To get all the ids from the collection you may try this:
$ids = $dues->pluck('id');
Also, you may add more constraints if you want if you use it like:the
$dues = $subscription->notifications_due()->where('some', 'thing')->get();
Or paginate:
$dues = $subscription->notifications_due()->where('some', 'thing')->paginate(10);

Laravel & Ardent - orderBy in $relationsData

In Eloquent, you can do this:
public function children() {
return $this->hasMany('page');
}
And in Ardent, you can do this:
public static $relationsData = [
'children' => [self::HAS_MANY, 'Page'],
];
In Eloquent, you can also do this:
public function children() {
return $this->hasMany('page')->orderBy('sort_order', 'desc');
}
Is there any way to do that, or get the same effect as that in Ardent?
I really like the short Ardent notation, but don't want to have to call $page->children->ordered() with a scope just to order them, as they'll need to be ordered every single time it's called anyway.
As it turns out, it's just not possible. You need the full definition if you want to do anything more than the basic relation functionality.

Categories