how to query whereHas on a belongs to many relationship using laravel - php

hi i am having a User and Task model and they have a many to many relation ship like below i added :
public function users()
{
return $this->belongsToMany(User::class,'user_tasks');
}
and in my user model :
public function tasks()
{
return $this->hasMany(Task::class);
}
and in my controller i want to get the task that are assigned to the logged in user like below :
$task = Task::whereHas('users', function(Builder $query){
$query->where('id',Auth::user()->id);
})->get();
dd($task);
but i get this error :
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous (SQL: select * from `tasks` where exists (select * from `users` inner join `user_tasks` on `users`.`id` = `user_tasks`.`user_id` where `tasks`.`id` = `user_tasks`.`task_id` and `id` = 4))
and when i change the id to users.id i get empty value but when i load it like below :
$task = Task::with('users')->get();
i get all the task with the relationships and they are working well but with whereHas its not working
thanks in advance

Since you are dealing with the pivot table with this relationship you can use the pivot table field user_id to filter:
$task = Task::whereHas('users', function (Builder $query) {
$query->where('user_id', Auth::user()->id);
})->get();

why not just:
$userTasks=Auth::user()->tasks;
this will get the current user tasks.

Related

Why is the request not working correctly when using BelongsToMany Laravel?

I have 2 models in my app, 'Subject' & 'Professor' (each Subject belongs to many Professors).
I made the many-to-many relation between two model using belongsToMany(). belongsToMany() doesn't work.
I'm trying to get data like this:
$subjects = Subject::with(["professors"])->whereHas("professors", function ($q){ $q->where("id", \request("professor_id")); })->get();
Error:
"SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'id' in where clause is ambiguous (SQL: select * from `subjects` where exists (select * from `professors` inner join `subjects_of_professor` on `professors`.`id` = `subjects_of_professor`.`professor_id` where `subjects`.`id` = `subjects_of_professor`.`subject_id` and `id` = 39))",
Does anyone know where did I make a mistake?
Here's the code to Models:
class Subject extends Model
{
public function professors(): BelongsToMany
{
return $this->belongsToMany(Professor::class, "subjects_of_professor");
}
}
class Professor extends Model
{
public function subjects(): BelongsToMany
{
return $this->belongsToMany(Subject::class, "subjects_of_professor");
}
}
And here is my database structure:
subjects:
id
title
subjects_of_professor:
id
subject_id
professor_id
professors:
id
name
description
I'm found mistake, i was needed to add table in my code, when i trying to get data:
$subjects = Subject::whereHas("professors", function ($q){ $q->where("professors.id", \request("professor_id")); })->get();

Eloquent add optional condition to hasMany where foreign key is null

How do you add an optional/OR condition to a eloquent relationship?
E.g I want all the users comments OR where the foreign key (user_id) is NULL.
select * from `comments` where (`user_id` is null OR `comments`.`user_id` in (1, 2, 3)) AND `status` = 1
In the User relationship added orWhereNull
public function comments() {
return $this->hasMany(Comments::class)->orWhereNull('user_id');
}
But Laravel it's running:
select * from `comments` where `user_id` is null and `comments`.`user_id` in (1, 2, 3)
Surprised this hasn't been asked before only thing I found similar was this:
https://laracasts.com/discuss/channels/eloquent/eloquent-orwherenull-when-where-has-no-results
I tried this but it needs the model not the query builder.
return $this->where(function ($query){
return $query::hasMany(Comment::class)->orWhereNull('user_id');
});
I'm using eager loading to fetch the comments for a list of users.
$users = User::with('comments')->where('active', 1)->paginate(10);
It doesn't work because the "orWhere" is sent to the underlying query builder immediately but the foreign key constraint is added when the query is run. I couldn't work out a nice way to sort that but this workaround is fine for my use case where I'm only selecting one row at a time with this relation (granted I should probably use replace it with a getter...):
(Excuse any mistakes, changing model names for clarity):
class Customer
{
public function selectedOrCurrentWeek(): HasOne
{
return $this->hasOne(Week::class, 'id', 'selected_week_id')
->withDefault(function (Week $instance, Customer $parent) {
return $instance->newQuery()
->whereRaw('CURRENT_TIMESTAMP between start_date and end_date')
->where('customer_id', $parent->id)
->first();
});
}
Query log when fetching a customer by ID :-
select * from `customers` where
`customers`.`id` = ?
and `customers`.`deleted_at` is null
limit 1;
select * from `weeks` where
CURRENT_TIMESTAMP between start_date and end_date
and `customer_id` = ?
and `weeks`.`deleted_at` is null
limit 1;
but it will run the second query once per row
You can optimize this further to your need, just giving an idea on query
$users = User::with('comments', function($query){
$query->where('user_id', '=', null)->where('status', '1');
})->paginate(10);

Integrity constraint violation: 1052 Column 'prof_id' in where clause is ambiguous Laravel

What I'm trying to do is very simple, Running a query with some relations and giving that relation a where clause.the query suppose to get questions with the related relations BUT the where clause on tags tells only get questions with the tag that been sent ($value).
userQuestion Model :
<?php
namespace App;
class userQuestion extends Model
{
protected $table = "question";
public $primaryKey = "question_id";
protected $fillable = ['question_id'];
public function gettags() {
return $this->belongsToMany('App\Profskills','question_profskill',"question_id","prof_id");
}
}
Query
$allquestions = userQuestion::with('getanswer','getusername','getbounty','gettags')
->whereHas('gettags', function($q) use($value) {
$q->where('prof_id', '=', $value);
})
->orderBy('created_at','Desc')
->get();
the problem is it gives me this error
QueryException in Connection.php line 729:
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'prof_id' in
where clause is ambiguous (SQL: select * from `question` where exists
(select * from `prof_skills` inner join `question_profskill` on
`prof_skills`.`prof_id` = `question_profskill`.`prof_id` where `question_profskill`.`question_id` = `question`.`question_id` and
`prof_id` = 1) order by `created_at` desc)
the column exist and if i switch the column to qp_id(PrimaryKey) it will work and the Columns are from the pivot table that im trying to access
Did some googling, what i did was :
1-put fillable with 'prof_id' in the model ( since i have a model for the pivot table too , did the same thing)
2-try =>where instead of whereHas
Still stuck,
Thanks for any help!
Add table name with your field as you have prof_id in both tables.
$allquestions = userQuestion::with('getanswer','getusername','getbounty','gettags')
->whereHas('gettags', function($q) use($value) {
$q->where('question_profskill.prof_id', '=', $value); //question_profskill or prof_skills
})
->orderBy('created_at','Desc')->get();

How replace join query with whereHas?

I am using Laravel 5.6 and i have a standard query
Order::where('dict_statuses_id', 1)
->leftJoin('dict_statuses', 'dict_statuses_id', '=', 'dict_statuses.id')
->get();
So i have list of orders with details where status = 1 (new) after join table i see status as New or Complete. Is any way to change this query using whereHas ?
i tried use
Order::whereHas('status', function($q){
$q->where('dict_statuses_id', 1);
})->get();
But no results, in model Order i have
public function status()
{
return $this->hasMany('App\DictStatuses');
}
[Updated]
i have an error:
Column not found: 1054 Unknown column 'dict_statuses.orders_id'
in 'where clause' (SQL: select * from `orders` where exists
(select * from `dict_statuses` where `orders`.`id` = `dict_statuses`.`orders_id` and `dict_statuses_id` = 1)
and `orders`.`deleted_at` is null)
In my table i have table Orders where is field: dict_statuses_id
and table dict_statuses with fields: id, public_name
Why error is about dict_statuses.orders_id
Try Order::has('status')->get();
You can pass in variables to a relationship so you could potentially do.
public function status($id) {
return $this->hasMany(DictStatuses::class)->where('dict_statuses_id', $id);
}
Try defining the local key & foreign key.
$this->hasMany(DictStatuses::class, 'foreign_key', 'local_key');
I think your dict_statuses doesn't have a column called order_id.
Please include that column in your migration
then call
Order::whereHas('status', function($q){
$q->where('dict_statuses_id', 1);
})->get();
in your controller
or change the relation in Order model as
public function status(){
return $this->belongsTo('App\DictStatuses'):
}

Laravel Builder Scope with Union and many-to-many relationship

I have a notifications table (and model)
notifications table columns are thus:
id
title
body
is_public
...
I also have a users table (and model)
users table columns:
id
username
...
I also have a pivot notification_user table
columns:
user_id
notification_id
many-to-many relationship is set on both Notification and User models thus:
Notification.php
public function users()
{
return $this->belongsToMany('App\Api\V1\Models\User');
}
User.php
public function notifications()
{
return $this->belongsToMany('App\Api\V1\Models\Notification');
}
Now inside Notification.php I want to set a scope. In the scope I need to get public notifications and the current user's
private notifications in a single SQL query. from my table structure, public notifications are where is_public == 1. Private notifications are associated on the pivot table.
to achieve this, inside my Notification.php, I also have this setup:
public function scopePublicAndPrivate(Builder $query)
{
return $this->public($query)->union($this->private($query));
}
public function scopePublic(Builder $query)
{
return $query->where('is_public', 1);
}
public function scopePrivate(Builder $query)
{
$user = JWTAuth::parseToken()->authenticate(); //using JWT to get a user.
return $user->notifications();
}
Now when I try Notification::publicAndPrivate()->get() inside a controller, I get:
Illuminate\Database\QueryException with message 'SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select * from `notifications` where `is_public` = 1) union (select * from `notifications` inner join `notification_user` on `notifications`.`id` = `notification_user`.`notification_id` where `notification_user`.`user_id` = 1))
Please I'll appreciate any help with getting this to work or a better solution.
I believe you should change:
return $user->notifications();
to something else, for example:
return $query->where('user_id', $user->id);
or maybe
return $query->whereHas('users', function($q) use ($user) {
$q->where('id', $user->id);
});
This is because in one query you are not using any join and in second you do and you are getting different number of columns for union parts.

Categories