Laravel Version: 5.6.39
PHP Version: 7.1.19
Database Driver & Version: mysql 5.6.43
Description:
When I chain where and orWhere in a model accessor to count related model , I get wrong result and here is my query. the count is returned strange result without filtering by the calling event id,
class Event extends Model
{
protected $table = 'events';
public function registrations()
{
return $this->hasMany('App\Components\Event\Models\Registration','event_id','id');
}
public function getSeatsBookedAttribute()
{
return $this->registrations()
->where('reg_status','=','Confirmed')
->orWhere('reg_status','=','Reserved')
->count();
}
}
Steps To Reproduce:
the following queries return me the expected results, however In my knowledge the first query should return the same result if i am not wrong, so i think this is a potential bug.
class Event extends Model
{
public function getSeatsBookedAttribute()
{
return $this->registrations()
->whereIn('reg_status', ['Confirmed', 'Reserved'])
->count();
}
}
class Event extends Model
{
public function getSeatsBookedAttribute()
{
return $this->registrations()
->where(function($query){
$query->where('reg_status','Confirmed')
->orWhere('reg_status','Reserved');
})
->count();
}
}
and here is the query dump,
here is the query when I donot explicit group it.
"select count(*) as aggregate from events_registration where (events_registration.event_id = ? and events_registration.event_id is not null and reg_status = ? or reg_status = ?) and events_registration.deleted_at is null "
and here is the query when i group it explicitly,
select count(*) as aggregate from events_registration where events_registration.event_id = ? and events_registration.event_id is not null and (reg_status = ? or reg_status = ?) and events_registration.deleted_at is null
The reason this happens is because you're chaining where() and orWhere(). What you don't see behind the scenes is a where event_id = :event_id applying to your query. You end up with a query that looks something like this:
select * from registrations where event_id = :event_id and reg_status = 'Confirmed' or reg_status = 'Reserved'
In normal SQL you'd want to put the last 2 conditions in parentheses. For Eloquent, you'd need to do something like this:
return $this->registrations()->where(function ($query) {
$query->where('reg_status', 'Confirmed')
->orWhere('reg_status', 'Reserved');
});
You can chain the toSql() method on these chains to see the difference. Note, that in this case, I believe whereIn() is the semantically correct thing to do.
Eloquent can handle this for you, though; scroll down to "Counting Related Models" in the Querying Relations part of the Eloquent Relationships docs:
$posts = App\Event::withCount([
'registrations as seats_booked_count' => function ($query) {
$query->where('reg_status','Confirmed')
->orWhere('reg_status','Reserved');
}
])->get();
Related
My user model has a 'prevregistration' attribute
public function prevregistration()
{
return $this->hasMany(Prevregistration::class, 'prevregistration_userid');
}
My prevregistraton model has a 'prev' attribute
public function prev()
{
return $this->hasOne(Prev::class,'prev_id', 'prevregistration_previd');
}
In my controller I show prevregistrations for the current user:
mynextprevs = Auth::user()->prevregistration ;
Now I want to only show prevregistrations from which the connected prev its prev_date in the future, like this:
$mynextprevs = Auth::user()->prevregistration::whereDate('prev_date', '>=', Carbon::today()->toDateString());
But then I get:
BadMethodCallException
Method Illuminate\Database\Eloquent\Collection::whereDate does not exist.
I also tried like this:
$mynextprevs = Auth::user()->prevregistration->prev::whereDate('prev_date', '>=', Carbon::today()->toDateString());
But then I get:
Property [prev] does not exist on this collection instance.
Should I/how can I filter the collection? I'm curious why Auth::user()->prevregistration->prev is not working, since that are attributes.
Thanks
You need to use the condition whereHas on your prevregistration
$mynextprevs = Auth::user()->prevregistration()->whereHas('prev',function($prev) {
$prev->whereDate('prev_date', '>=', Carbon::today()->toDateString());
})->get();
Notice we used the relation as a method prevregistration() to access it as a query builder and not as a collection hence the need for the ->get() at the end.
I've this method:
public function indexGuest($idAzienda, Request $request){
$companyId = $idAzienda;
$ddts = Ddt::where('company_id',$companyId);
$ddt_urls= Ddt_file_url::all();
if($request->start_date || $request->end_date){
$ddts->whereBetween('created_at',[new Carbon($request->start_date),new Carbon($request->end_date)]);
}
$ddts->paginate(10);
return view('guest.ddt-management')->with('ddts', $ddts)->with('ddt_urls',$ddt_urls)
->with('companyId',$companyId);
}
My start_date and end_date comes in strings like "yyyy-mm-dd".
I've tried to pass it straight to the query and like in the example like a carbon object with no hope!
After executing the query (now only the one without the wherebeeteween clause) i cant apply the method "paginate" to the collection, no error are raised but when i pass it to the view, the "link()" method not work and raise an error again.
where I wrong?
Laravel 5.4
Structure your wheres like this.
public function indexGuest($idAzienda, Request $request) {
[...]
$ddts = Ddt::where('company_id', $companyId)
->where(function($query) use ($request) {
if($s = $request->get("start_date") {
$s_date = Carbon::parse($s)->format("Y-m-d");
$query->whereDate("created_at", ">=", $s_date);
}
if($e = $request->get("end_date") {
$e_date = Carbon::parse($e)->format("Y-m-d");
$query->whereDate("created_at", "<=", $e_date);
}
})
->paginate(10);
[...]
}
this is my query
$personnel_info = \DB::table('assigns AS a')
->join('boxes AS b','b.id','=', 'a.box_id')
->join('positions AS p','p.id','=', 'b.position_id')
->select('a.id','b.id AS box_id','p.id as position_id','p.title','a.status','a.end_date')
->where('a.personnel_id','=',$personnel_id)
->get();
and this realtionship for boxes:
class Boxes extends Model
{
public function position()
{
return $this->belongsTo('Positions');
}
public function assign()
{
return $this->hasOne('Assigns', 'box_id');
}
}
how to use eloquent query(also realtionship) for replace DB facade query?
i want select some field for tables.without define fileds in boxes model
tnx
Try
$personnel_info = Assign::with('box.position')
->where('personnel_id', $personnel_id)
->get();
Then dd($personnel_info) to see everything that was returned. If you don't like the values, then add your select() clause.
I'm having two tables as 'jobs' and 'desired_skills'.
Table structure is as follows.
jobs table
jobs Table
desired_skills table
desired_skils table
where desired_skills.job_id refers to jobs.job id
In controller I have (I am getting $id as an argument from the url, and I can confirm the argument grabs the desired value)
$jobs = DB::table('jobs')->where(function ($query) use ($id) {
$query->Join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->where('jobs.employer_id', '=', $id);
->select('*')
})->get();
when I dump and die $jobs it only returns values from jobs table.
but when I run the query
SELECT * FROM jobs INNER JOIN desired_skills ON desired_skills.job_id = jobs.job_id it returns the desired value set.
What am I doing wrong? Any help will be greatly appreciated.
I think it has to do with wrapping your join inside of a where clause. I don't think it's giving you your desired query with that there.
$jobs = DB::table('jobs')
->join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->where('jobs.employer_id', '=', $id)
->get();
The query SELECT * FROM jobs INNER JOIN desired_skills ON desired_skills.job_id = jobs.job_id
is not the same has what you are trying to do in the function. In this query there is not
mention of 'employer_id' in the table 'jobs'.
An alternative would be to use eloquent relationships, as refered in a comment.
You need 3 classes in models:
Employer
Job
DesiredSkill
Between Employer and Job -> one-to-many relation (an employer can have multiple jobs).
Between DesiredSkill and Job -> one-to-one relation.
I'm not sure what you are trying to get from the join, but i think that if you implement
the methods that allow the relationships i believe you solve whatever.
class Job extends Model
{
public function employer()
{
return $this->hasOne('App\Job');
}
}
class Employer extends Model
{
public function jobs()
{
return $this->hasMany('App\Employer');
}
public function desiredSkill()
{
return $this->hasOne('App\DesiredSkill');
}
}
class DesiredSkill extends Model
{
public function job()
{
return $this->hasOne('App\DesiredSkill');
}
}
Try this:
$jobs = DB::table('jobs')
->join('desired_skills', 'desired_skills.job_id', '=', 'jobs.job_id')
->select('jobs.*', 'desired_skills.*')
->get();
How can I make relationship with union in laravel eloquent? I've already tried two different approaches.
User::with(['url' => function($query) use(&$some_property) {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $some_property);
$query = $query->union($favouriteUrls);
}]);
In the first attempt there wasn't any union in the query. Then I tried to move the logic to the model.
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', $this->some_property);
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
It has executed successfully but $this->some_property was set inside the query to the null value.
I can't create two separate relationship in this case. It has to be one with union. How can I fix it?
If you call that relation as User::with('urls') you will get that $this->some_property doesn't exists, because the object itself doesn't exists. But if you call the urls() method on an object, it should work. Something like this:
$user = User::find(1);
$user->urls; // here $this->some_property should have a value
Assuming you're calling the urls() method from a User object, the $this->some_property should give you the value. If for some reason you cannot access a property directly on an Eloquent model you can always refer to the attributes[] array inside of the model. For example
// calling
$this->some_property
// should be the same as
$this->attributes['some_property']
Fetch all the users joining with the condition
Assuming users is the table for all the users, in your query you could change $this->some_property with 'users.some_property' and everything should work as expected, for each user it will query based on that property. Here is the code:
class User extends \Eloquent {
public function urls() {
$favouriteUrls = \DB::table('urls')
->select('urls.*')
->join('favourite_urls', function($join) {
$join->on('favourite_urls.url_id', '=', 'urls.id');
})
->where('some_condition', '=', 'users.some_property');
return $this->belongsTo('Url')->union($favouriteUrls);
}
}
And then just call the method like this:
User::with('urls')->get();