orderBy created_at on many-to-many relationship laravel and eloquent - laravel

I would like to know how can I use an orderby on created_at on pivot table.
Currently I have this :
users
events
subscriptions [user_id, event_id, created_at, updated_at]
I would like to have all users subscriptions on specific event id :
return $this->user
->with('subscriptions')
->whereHas('subscriptions', function ($query) use ($eventId) {
$query->where('events.id', $eventId)
->orderBy('subscriptions.created_at', 'ASC');
})
->paginate(5);
I have all users attach on a specific event but the orderBy doesn't work.
I would like to use the whereHas and no the join method.

I believe you should order on the with clause, like so:
return $this->user
->with([
'subscriptions' => function ($query) {
$query->orderBy('created_at', 'ASC');
}
])
->whereHas('subscriptions', function ($query) use ($eventId) {
$query->where('events.id', $eventId);
})
->paginate(5);

Here's another way to get what you want:
join()
return $this->user
->with('subscriptions')
->has('subscriptions')
->join('subscriptions', function ($join) use ($request) {
$join->on('subscriptions.user_id', '=', 'users.id');
})
->select('users.*', 'subscriptions.created_at as subscriptions_created_at')
->orderBy('subscriptions_created_at', 'ASC')
->paginate(5);

with() uses eager loading, which turns query into multiple queries. You can try this.
return $this->user->join('subscriptions', function ($join) use ($eventId) {
$join->on('subscriptions.user_id', '=', 'users.id');
$join->where('events.id', $eventId)
})
->select('users.*', 'subscriptions.*')
->orderBy('subscriptions.created_at', 'ASC')
->paginate(25);

I'm hoping this helps you my friend :
return $this->user
->with(['subscriptions' => function ($query) use ($eventId) {
$query->where('event_id', $eventId)
->orderBy('created_at', 'ASC');
}])
->paginate(5);

Try to use the hasManyThrough option. This will find all events that are intermediate of the subscriptions pertaining to the user.
User extends Model{
public function ScopeEvents()
{
return $this->hasManyThrough(Event::class, Subscription::class);
}
}
UserController extends Controller{
public function listEvents(Request $request, User $user){
$event_id = $request->get('event_id');
return $user->events()->where('event_id',$event_id)
->orderBy('subscriptions.created_at','ASC')->paginate(5);
}
}

Related

Laravel where of Relationship from Relationship

I have easy relationships like this
Project Model:
public function milestones()
{
return $this->hasMany('App\Models\Milestone');
}
Milestone Model:
public function milestones()
{
return $this->hasMany('App\Models\Milestone');
}
Now I try to get Todo with id = 3. And it should only show Milestone where my Todo is inside. This is my solution at the moment (shows all Milestones inside project)
$query = Project::whereHas('milestones',function($query) use ($UserId){
$query->whereHas('todo', function ($query) use ($UserId){
$query->where('id',3);
});
})->with('milestones.todo',function($query){
$query->where('id',3);
})
->get();
How do I limit milestones to that one, where todo with id 3 is inside?
You'll need to be explicit with your eager load:
$query = Project
::with('milestones', function ($query) use ($UserId) {
$query
->with([
'todos' => function ($query) use ($UserId) {
$query->where('id', $UserId);
},
])
->whereHas('todos', function ($query) use ($UserId) {
$query->where('id', $UserId);
});
})
->whereHas('milestones', function ($query) use ($UserId) {
$query->whereHas('todo', function ($query) use ($UserId) {
$query->where('id', $UserId);
});
})
->get()

orderBy on hasMany relationship 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);
});
}

Nested "with()" laravel

I'm having issues with some relationships. I have the following situation
Product:
public function catalogues()
{
return $this->belongsToMany('App\Catalogue');
}
public function category()
{
return $this->belongsTo('App\Category');
}
Category:
public function products()
{
return $this->hasMany('App\Product');
}
Catalogue:
public function products()
{
return $this->belongsToMany('App\Product');
}
With a single query, I need to get all the categories that have products belonging to a certain catalogue. How can I achieve it?
Use whereHas():
Category::whereHas('products.catalogues', function($q) use($catalogueId) {
$q->where('catalogues.id', $catalogueId);
})
->get();
Or:
Category::whereHas('products', function($q) use($catalogueId) {
$q->whereHas('catalogues', function($q) use($catalogueId) {
$q->where('id', $catalogueId);
})
})
->get();
You need whereHas and
Category::whereHas('products')
->with(['products' => function($query) use ($id){
$query->with('catalogues' => function($query) use ($id){
$query->where('catalogues.id',$id);
});
}])
->get();
Hope this helps.

Eloquent: hasNot with parameter

I have the following eloquent models:
User | id
Post | id
Comment | id | post_id | user_id
Using eloquent, how can I fetch all Posts which a specific User hasn't commented yet?
I tried so far:
In Model Post:
public function noCommentOf(User $user) {
$this->hasNot('App\Comment')->commentOf($user);
}
In Model Comment:
public function commentOf($query, User $user) {
return $query->where('user_id', '=', $user->id);
}
The way I would do this is by querying the Post model with a whereDoesnthave relationship. In your controller:
public function getPostsWithoutCommenter(){
$userId = 1; // Could be `$user`, `use($user)` and `$user->id`.
$posts = \App\Post::whereDoesntHave("comments", function($subQuery) use($userId){
$subQuery->where("user_id", "=", $userId);
})->get();
}
This would assume that comments is defined on the Post model as:
public function comments(){
return $this->hasMany(Comment::class);
}
Basically, if the comments relationship with the check for that $userId returns a Collection, it would be ignored from the result set.
Post model
public function comments()
{
return $this->hasMany(Comment::class)
}
Then get posts
$posts = Post:: whereDoesntHave('comments', function ($query) use ($userId) {
$query->where('user_id', $userId);
});
To get posts with no comments
$posts = Post::has('comments', '=', 0)->get();
I think:
$user->post()->leftJoin('comments', 'posts.id', '=', 'comments.post_id')->whereNull('comments.id');

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