orderBy on hasMany relationship laravel - laravel

I made a "hasMany" relationship from Category model to Product model using ProductCatRel model.
I am trying to ordering my products form Category model. The "where" condition is fine, But "orderBy" is not working. Here is my code:
public function Products(){
return $this->hasMany(ProductCatRel::class,'category')
->with('Product')
->whereHas('Product', function($q){
$q->where('status', 1)->orderBy('position');
});
}

Use the following snippet may works
public function products(){
return $this->hasMany(ProductCatRel::class,'category')
->with('Product')
->whereHas('Product', function($q){
$q->where('status', 1)
});
}
$products = App\Category::find(1)->products()->orderBy('position')->get();

whereHas() only check existence and don't affect on retrieved relation data.
You should apply orderBy() in with() method. Also you need to duplicate status checking in with() method.
public function Products(){
return $this
->hasMany(ProductCatRel::class, 'category')
->with(['Product' => function ($q) {
$q->where('status', 1)->orderBy('position');
}])
->whereHas('Product', function($q) {
$q->where('status', 1);
});
}

Related

Sort inside the with in laravel

$posts = PostUrl::with(['post' => function ($q) {
$q->orderBy('created_at', 'DESC');
}])
->where('category','like' ,'%'.$cat.'%')
->paginate(8);
How can I sort the result as per the created_at of the post inside?
When try to ->orderBy('post.created_at','DESC') it show column not found.
Add PostUrl models
public function post(){
return $this->hasMany('App\Post', 'postid')->orderBy('created_at', 'DESC');
}
Controller :
$posts = PostUrl::with(['post'])->where('category','like' ,'%'.$cat.'%')->paginate(8);
Make sure you have established correctly in your PostUrl model the relationship with your Post model (if it is so called). You should have a function similar to the following in your model in order to with() method works correctly in the controller:
public function post(){
return $this->hasMany('App\Post', 'postid');
}
This way there should be no problem for the orderBy clause to work properly.

Order on second degree relationship in Eloquent

I have two models in a many-to-many relationship: Fixture and Event.
Fixture:
public function events()
{
return $this->belongsToMany(Event::class, 'fixture_events')->withPivot('player_id');
}
Event:
public function fixtures()
{
return $this->belongsToMany(Fixture::class, 'fixture_events')->withPivot('player_id');
}
You will notice that the pivot table has an additional field player_id. This is because FixtureEvent also had a relationship to a model called Player.
FixtureEvent:
public function fixture()
{
return $this->hasOne(Fixture::class, 'id', 'fixture_id');
}
public function event()
{
return $this->hasOne(Event::class, 'id', 'event_id');
}
public function player()
{
return $this->belongsTo(Player::class, 'id', 'player_id');
}
And Player has:
public function events()
{
return $this->hasMany(FixtureEvent::class);
}
My problem arises when I want to get all the fixture_events for a player and sort them by a field in the events table. This field is named sequence.
However, whatever I do, the events always come out ordered by ID.
This is the query that I would like to order by events.sequence, whether by using some type of join or whatever works (this is inside the Player model so $this is a player object):
$events = $this->events()->whereHas('fixture', function ($query) use ($round, $competition_id) {
$query->where('fixtures.round', '=', $round)->where('competition_id', $competition_id);
})->get();
I've tried adding a join query here on fixture_events.event_id = events.id and then ordering by events.sequence but this doesn't work.
I've also tried adding orderBy directly in the model relationship, i.e. in the Fixture model:
public function events()
{
return $this->belongsToMany(Event::class, 'fixture_events')->orderBy('sequence')->withPivot('player_id');
}
But this does nothing for my problem.
How do I make this happen?
Update
At first I misread the relations, can you try with the below query?
$events = $this->events()->whereHas('fixture', function ($query) use ($round, $competition_id) {
$query->where('fixtures.round', '=', $round)->where('competition_id', $competition_id);
})->with(['events.event' => function ($query) {
$query->orderBy('sequence');
}])->get();
You have a couple of alternatives, but first I suggest you to edit your relationship to include the sequence field you are trying to load.
Then proceed with one of the following:
Order by on the relationship definition, but I think you have to load that field from the pivot table, otherwise you won't have its value, and prefix the relations table on the orderby field.
public function events() {
return $this->belongsToMany(Event::class, 'fixture_events')
->withPivot(['player_id', 'sequence'])
->orderBy('fixture_events.sequence');
}
or with:
public function events() {
return $this->belongsToMany(Event::class, 'fixture_events')
->withPivot(['player_id', 'sequence'])
->orderBy('pivot_sequence');
}
Order by a pivot field outside the relation can be done like this:
$events = $this->events()->whereHas('fixture', function ($query) use ($round, $competition_id) {
$query->where('fixtures.round', '=', $round)->where('competition_id', $competition_id);
})->with(['fixture' => function ($query) {
$query->orderBy('sequence');
}])->get();
or with:
$events = $this->events()->whereHas('fixture', function ($query) use ($round, $competition_id) {
$query->where('fixtures.round', '=', $round)->where('competition_id', $competition_id);
})
->orderBy('pivot_sequence')
->get();
Let me know if any of these methods works!

Laravel - How to get data from a table related with pivot table

I have this model for notifications table:
class Notification extends Model
{
public function users()
{
return $this->belongsToMany(User::class, 'notification_user', 'notification_id', 'user_id');
}
}
And this method in controller for getting data from notifications where id of notifications is related to a pivot table named notification_user:
$myNotifications = DB::table('notification_user')
->join('notifications', 'notifications.id', 'notification_user.notification_id')
->where('notification_user.user_id', $userId)
->where('notification_user.seen', 0)
->get();
the result of $myNotifications is correct but I want to use Model and its relationship instead of DB.
How can I get all records in notifications where each notification related to a specific user which is not seen by the user.
You need to add ->withPivot('seen') to the relationship:
public function users()
{
return $this
->belongsToMany(User::class, 'notification_user', 'notification_id', 'user_id')
->withPivot('seen');
}
Then you can do:
Notification::whereHas('users', function ($q) use ($userId) {
$q->where('id', $userId)->where('seen', 0);
})->get();
To avoid joining users, your other option is whereExists:
Notification::whereExists(function ($q) use ($userId) {
$q
->selectRaw(1)
->table('notification_user')
->whereRaw('notifications.id = notification_user.notification_id')
->where('user_id', $userId)
->where('seen', 0);
})->get();
Should still be more performant, but not much more elegant.
You will have to define the same relation in User model as notifications and then:
$notifications = User::where('id', $user_id)->notifications()->where('seen', 0)->get();
You can use with keyword for eager loading inside your controller.
like if you have any relation defined inside your model, just add a with('modelRelation') before your get() statement in eloquent.
Happy Coding.

Get indirect relationship from collection

I have:
User->hasMany('Order')
...
Order->hasMany('Product')
...
$users=User::with('orders','orders.product')->...
how can I retrieve all products bought from users in $users taking advantage of eager loading instead of doing other queries?
You could use a hasManyThrough relationship on your User model.
https://laravel.com/docs/5.4/eloquent-relationships#has-many-through
public function products()
{
return $this->hasManyThrough('App\Product', 'App\Order');
}
or, using a collection, as your already eager loading.
$products = User::with('orders','orders.product')->flatMap(function(User $user) {
return $user->orders->flatMap(function(Order $order) {
return $order->products;
});
});

Querying a related model within a Laravel query scope

I have a query scope method that filters results on a Foo model by the author of the model. The complication is that the author is not directly related.
Foo belongs to Bar and Bar belongs to User. If Foo belonged to User, I could just do this:
public function scopeAuthorOnly($query, User $user)
{
return $query->whereuser_id($user->id);
}
This obviously won't work as the Foo model has no user_id column and instead has a bar_id column. However I'm not sure how I can build the query in a way that would filter the user_id.
Any pointers?
You can use whereHas() in scopes to query relationships.
Let's assume you have a users() relation function in this model. Then it'd be something like this:
public function scopeAuthorOnly($query, User $user)
{
return $query->whereHas('users', function($q) use($user){
$q->where('id', $user->id);
});
}
Update: You can also nest whereHas statements: If the current Foo has a relation named bar, and bar has a relation called users, it'd be like this:
public function scopeAuthorOnly($query, User $user)
{
return $query->whereHas('bar', function($q) use($user){
return $q->whereHas('users', function($q2) use($user){
$q2->where('id', $user->id);
});
});
}
More info: here
You can also do this using relations (without resorting to manual joins):
public function scopeAuthorOnly($query, User $user)
{
$query->whereHas(array('Bar' => function($query) use($user){
$query->where('user_id', '=', $user->id);
}));
}
Edit: now using whereHas instead of with (Laravel >= 4.1 only). Thanks for the correction Arda.
Edit: in 4.2 the syntax of whereHas has changed, so that the relationship name is parameter 1 and the closure is parameter 2 (rather than being passed in as an array):
public function scopeAuthorOnly($query, User $user)
{
$query->whereHas('Bar', function($query) use($user){
$query->where('user_id', '=', $user->id);
});
}
Found a solution:
public function scopeAuthorOnly($query, User $user)
{
$query
->join('bars', 'foos.bar_id', '=', 'bars.id')
->join('users', 'bars.user_id', '=', 'users.id')
->where('users.id', '=', $user->id);
return $query;
}

Resources