Laravel latest not working (not appearing in my SQL query) - php

So I have a Student model with this function:
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->latest();
}
then I just do a query with this latestStatus()
$query = Student::findOrFail(1);
$query = $query->whereHas('latestStatus', function($query) use ($statusuri) {
$query->where('status_id', 1);
});
dd($query->toSql());
and the toSql() function returns:
"select * from `students` where exists (select * from `status_student` where `students`.`id` = `status_student`.`student_id` and `status_id` = ?)
as if latest() is ignored.
Why doesn't latest() add anything to the query?
Thanks.
Edit:
I tried adding selectRaw for example:
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->selectRaw('MAX(status_student.id)');
}
and still nothing appears in my query.

If you dig deeper to the whereHas() relationship. It calls the has() method then if you look for the has() method you will see the getRelationWithoutConstraints() method, means that it will call the relationship but it will remove all the constraints attach to it and will only call the base query instance :
public function latestStatus()
{
return $this->hasOne(StatusStudent::class)->latest(); // the latest() will be removed in the query if you call the `latestStatus` using the `whereHas() or has()`
}
so if you use the whereHas() like the way you use it :
"select * from `students` where exists (select * from `status_student` where `students`.`id` = `status_student`.`student_id` and `status_id` = ?)
it will return the query with out the latest().
Instead of doing it like that you can do it like :
Student Model
public function status() : HasOne
{
return $this->hasOne(StatusStudent::class);
}
Controller
$student = Student::findOrFail(1);
$student->whereHas('status', function($query) {
$query->where('status_id', 1)
->latest();
})
But since the relationship is define as one-to-one :
$student = Student::findOrFail(1);
$student->load('status');
or
$student = Student::findOrFail(1)->status()->get();
Maybe you want to get the latest of all the status.
StudentStatus::query()->latest()->get();

As stated in a comment by #matticustard,
findOrFail() returns a model, not a query builder.
Instead of findOrFail(1) use where('id', 1)

Related

Multiple Joins in Eloquent from local scopes

Got a question regarding Eloquent and the scope functionality:
Assuming two scopes:
class Result extends Model {
public function scopeIsRace($query) {
return $query
->join('sessions', 'sessions.id', '=', 'results.session_id')
->where('sessions.type', 10)
}
public function scopeIsOfficial($query) {
return $query
->join('sessions', 'sessions.id', '=', 'results.session_id')
->join('events', 'events.id', '=', 'sessions.event_id')
->where('events.regular_event', 1);
}
}
Calling both of them performs two joins of sessions and the resulting query looks sth like this (doesnt work)
select * from `results` inner join `sessions` on `sessions`.`id` = `results`.`session_id` inner join `sessions` on `sessions`.`id` = `results`.`session_id` inner join `events` on `events`.`id` = `sessions`.`event_id` where `driver_id` = 24 and (`sessions`.`type` = 10 or `sessions`.`type` = 11) and `events`.`regular_event` = 1
How do I prevent the double join on sessions?
Thank you so much #Nima. Totally forget about an advanced whereHas. Used a structure llke this from your suggested question and it works perfectly fine:
public function scopeIsRace($query) {
return $query->whereHas('session', function($query){
$query->where('type', 10);
});
}
public function scopeIsOfficial($query) {
return $query->whereHas('session', function($query) {
return $query->whereHas('event', function($query2) {
$query2->where('regular_event', 1);
});
});
}

Overriding where conditions in Laravel Query builder

I am working on E-Commerce application, in which I am fetching the product which has the status 1.
But there are situations where I need to fetch the product which has status 2 or 3.
I have overridden the newQuery method and added a condition for status 1 in that method. Now I want to add another condition for status 2, but when I add this condition, the where clause will be duplicated in SQL query for that column(the default behavior of query builder).
Below is my code,
public function newQuery($excludeDeleted = true) {
return parent::newQuery($excludeDeleted)
->where('status', 1);
}
When I need to fetch the product with status 2 as below
return $query->where('status', 2);
then the query formed will be as below
select * from `products` where `status` = 1 and `status` = 2
But I need an output like below query
select * from `products` where `status` = 2
Please suggest the way of doing it.
Add a parameter to the newQuery function and pass in the status. When calling the newQuery function the default status would be 1, but if you supply an argument it will be whatever you pass to it.
public function newQuery($excludeDeleted = true, $status = 1) {
return parent::newQuery($excludeDeleted)
->where('status', $status);
}
EDIT
This may be possible using Dynamic Local Query Scope:
//Model
public function scopeStatus($query, $status = 1)
{
return $query->where('status', $status);
}
and call like:
$result = App\MyModel::status(2)->get();
//OR To get with default "status"
$result = App\MyModel::status()->get();

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.

How can I fetch Laravel models by matching a value on a relationship?

I have a User model with attributes like login, ID, age, etc. Another table is user_data with a residence column, for example.
How can I get all users with a specific residence? I have this:
User model:
public function user_data()
{
return $this->hasMany('App\Models\UserData');
}
public function FilterUser($request)
{
if ($request->has('city'))
{
$users = User::with('user_data')->where('residence', 'Warsaw')->get();
}
dd($users);
}
UserData model:
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
Now I get this error:
Column not found: 1054 Unknown column 'residence' in 'where clause' (SQL: select * from `users` where `residence` = warsaw and `users`.`deleted_at` is null)
And another question:
If User is my main model, should I connect it with relationships like above? hasMany() or belongsTo()?
Use the whereHas() query builder method:
$users = User::with('user_data')
->whereHas('user_data', function($query) {
$query->where('residence', 'Warsaw');
})
->get();
Edit - To use the value from $request you need to import the variable to the closure for whereHas():
...
->whereHas('user_data', function($query) use ($request) {
$query->where('residence', $request->city);
})
...
Edit - A query scope may be a better approach. In the User model, add this method:
function scopeInCity($query, $city)
{
return $query->whereHas('user_data', function($q) use ($city) {
$q->where('residence', $city);
});
}
Then you can filter all users by city (outside the model):
User::inCity($request->city)->get();
Use whereHas if you want to get all users that have a relationship with a specific attribute like so:
$users = User::whereHas('user_data', function($q) {
$q->where('residence', 'Warsaw');})->get();

Query with pivot table laravel 5.1

Query with pivot table laravel
Mappings
I am having a trouble with pivot query.
App/Entities/user.php
public function roles()
{
return $this->belongsToMany('App\Entities\UserRole', 'user_user_roles', 'user_id', 'user_role_id');
}
public function getPivotCondition($roleId)
{
return $this->roles()->wherePivot('user_role_id','=',$roleId);
}
Controller
use App/Entities/user;
public function getUserWithRole(user $User){
$users = $User->getPivotCondition(1)->get();
dd($users);
}
It results in a query
SELECT `user_roles`.*,
`user_user_roles`.`user_id` AS `pivot_user_id`,
`user_user_roles`.`user_role_id` AS `pivot_user_role_id`
FROM `user_roles`
INNER JOIN `user_user_roles`
ON `user_roles`.`id` = `user_user_roles`.`user_role_id`
WHERE `user_user_roles`.`user_id` IS NULL
AND `user_user_roles`.`user_role_id` = '1'
my question is that why i am getting user_user_roles.user_id is null in my query
I know that i can use WhereHas but i am looking for an alternative.I can use wherePivot('user_role_id','=',$roleId,'or'); else orWherePivot but it will result in query condition to or.

Categories