Eloquent: hasNot with parameter - laravel

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

Related

Laravel Single User Data In A Multichained Distant Relationship

I have three models as per the code below.I have a problem of returning a single savings data for authenticated users. Any help will be highly appreciated.
I want a low like this:
$savings =Savings::client()->client_users()->where('user_id', '=', Auth::user()->id)->get();
Savings Model
class Savings extends Model
{
public function client()
{
return $this->hasOne(Client::class, 'id', 'client_id');
}
}
Client Model
class Client extends Model
{
public function client_users()
{
return $this->hasMany(ClientUser::class, 'client_id', 'id');
}
}
Client User Model
class ClientUser extends Model
{
public function user()
{
return $this->hasOne(User::class, 'id', 'user_id');
}
}
You can try this query:
$user = Auth::user();
return Savings::whereHas('client.client_users', function ($query) use ($user){
$query->where('user_id', $user->id);
}])->get();
I have fixed the above error. Thanks #Maik Lowrey
$user = Auth::user();
$savings= Savings::whereHas('client.client_users', function ($query) use ($user){
$query->where('user_id', $user->id);
})->get();
return response()->json($savings);
//return response($savings);

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

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

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

Laravel: Find all posts made by a user using name field

I want to fetch all posts made by a user by matching the name of the user from the route url using Eloquent
My User.php has relationship as follows
public function posts() {
return $this->hasMany('App\Post');
}
My Post.php model has following relationship:
public function user() {
return $this->belongsTo('App\User');
}
my route is like following:
Route::get('/users/{name}', 'UsersController#show');
I know if I want to fetch all posts by a user I need to do something like this
public function show($name) {
$user = User::find(1);
$posts = $users->posts()->get();
return view('users.show', compact('posts'));
}
but I want to find the user based on the name field in the database and not the id
I cant use ->posts() if I use where like Users::where('name', $name)->posts()->get(); , It throws error.
How do I fetch all posts of a user by matching his name in database with eloquent
You can use whereHas():
$posts = Post::whereHas('user', function($q) use($name) {
$q->where('name', $name);
})->get();
Or you can do this:
User::where('name', $name)->first()->posts()->get();
Or:
$user = User::where('name', $name)->first();
$posts = Post::where('user_id', $user->id)->get();
All these options will create two queries.

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