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();
Related
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)
I have got a relationship where I select all row's based on the category, however I need to exclude some of these if they are within a sub query.
/** #var \Illuminate\Database\Query\Builder $images */
$images = $vehicle->images()
->whereIn('image_category', $website->image_categories)
->orderBy('seq', 'ASC');
$images->whereNotIn('id', static function ($q) {
return $q->select('id')
->whereIn('image_category', [0, 99])
->groupBy('seq')
->having(DB::raw('count(`seq`)'), '>', 1);
});
dd($images->toSql(), $images->getBindings());
So above is my code, nearly works as I want it, however it seems that the $q variable doesn't have the table name within the query, below is the query outputted:
select
*
from
`vehicle_images`
where
`vehicle_images`.`vehicle_id` = ?
and `vehicle_images`.`vehicle_id` is not null
and `image_category` in (?, ?)
and `id` not in (
select
`id`
where
`image_category` in (?, ?)
group by
`seq`
having
count(`seq`) > ?
)
order by
`seq` asc
This is the relationship:
public function images()
{
return $this->hasMany(VehicleImage::class);
}
You can specify what table you want to use.
$images->whereNotIn('id', static function ($q) {
return $q->select('id')->from('{CORRECT_TABLE_NAME_HERE}')
->whereIn('image_category', [0, 99])
->groupBy('seq')
->having(DB::raw('count(`seq`)'), '>', 1);
});
I don't know what exactly the table name should be, hence the placeholder.
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);
I'm using Symfony 4 with EasyAdmin and I need to make a select query that retrieve all the users, but I want the connected user to be selected as the first result.
I don't know how to proceed at all. I was thinking maybe do multiple select in the same query ? but I don't even know how to do this.
So far I have this :
$repo = $this->getDoctrine()->getRepository(User::class);
$authUser = $this->getUser();
$queryBuilder = $repo->createQueryBuilder('u');
return $queryBuilder;
// Doctrine createQueryBuilder looks like this
public function createQueryBuilder($alias, $indexBy = null)
{
return $this->_em->createQueryBuilder()->select($alias)->from($this->_entityName, $alias, $indexBy);
}
EDIT : I imperatively need to return the queryBuilder object, not the result set, that's why it's tricky.
An approach that will query all users, but gives you an array in the order you describe:
$users = $userRepository->findall();
foreach ($users as $user) {
$this->getUser() == $user ? $currentUser[] = $user : $otherUsers[] = $user;
}
$myUserList = array_merge($currentUser, $otherUsers);
Two sidenotes: 1: This queries all users and then splits them. I'm not sure if this could be what you want. 2: if no user is currently logged in this code will not work because $currentUser won't be defined. Of course, you can change it so it will also work when no user is logged in.
Edit based on the fact that returning the querybuilder is imperative:
You can use CASE to test and create a property, which you can hide with AS HIDDEN in your query and then order by this hidden property like so:
public function currentUserFirst($userId){
$qb = $this->createQueryBuilder('u')
->select('u, CASE WHEN u.id = :userId THEN 0 ELSE 1 END AS HIDDEN order')
->setParameter('userId', $userId)
->orderBy('order', 'ASC');
return $qb;
}
As the first comment pointed out, you will need to use a UNION, which is not supported in DQL.
Therefore your only option is to use a native query using the Connection which you need to get from the EntityManager.
It would look something like this:
$id = $this->getUser()->getId();
$sql = 'SELECT * FROM `users` WHERE `id` = :id UNION SELECT * FROM `users` WHERE`id` != :id'
$users = $this->getDoctrine()->getConnection()->fetchAll($sql, compact('id'));
You can use setMaxResults:
$qb->andWhere('q.start <= :start')
->setParameter(':start', $value)
->setMaxResults(1)
;
I have two models, App\Song (belongsTo App\Host) and App\Host (hasMany App\Song).
In my controller I am using the following query:
$songs = Song::whereHas('host', function($query) {
$query->eligable()->activeHost();
})->inDownloadedQueue()->get();
This is derived from the following query scopes in my Song.php model
public function scopeEligable($query)
{
$query->where('skip_threshold', '>', \DB::raw('songs.attempts'));
}
public function scopeActiveHost($query)
{
$query->where('active', 1);
}
public function scopeInDownloadQueue($query)
{
$query->whereNull('downloaded');
}
This doesn't return any results, turning to ->toSql() function to debug the query is as follows:
select * from "songs" where exists (select * from "hosts" where "songs"."host_id" = "hosts"."id" and "skip_threshold" > songs.attempts and "active" = ?) and "downloaded" is null
This host = ? seems to be the problem. Any ideas why this is?
The ? is a SQL parameter. Eloquent uses PDO parameterized queries, which helps prevent SQL injection.
The query itself will show active = ?, but when the query is executed, it will bind your value (1) to this parameter, and the query that is actually executed will be active = 1.
The queries are fine, you're just not getting any results because you don't have any songs in your downloaded queue that are related to eligible, active hosts.