Queries in Laravel Logic operators - php

I want to query Laravel DB for something like
SELECT * FROM `tickets`
WHERE (created_at > '2015-07-01'
AND created_at < '2015-07-31')
AND (state != 'Resolved'
OR state != 'closed'
OR state != 'Cancelled'
OR state != 'Solution Rejected')
ORDER BY `id` DESC
I tried using raw statements but doesn't seem to work. So I'm trying to use Laravel's own model functions to archive the same result, but I'm missing something... here's what I got so far.
Ticket::whereBetween('created_at', [$start, $end])
->whereIn('state',['Resolved','closed','Cancelled','Solution Rejected'])
->get();
PS: alternatively is there a way I can have this instead
SELECT * FROM `tickets`
WHERE ( (created_at > '2015-07-01'
AND created_at < '2015-07-31')
OR (updated_at > '2015-07-01'
AND updated_at < '2015-07-31') )
AND (state != 'Resolved'
OR state != 'closed'
OR state != 'Cancelled'
OR state != 'Solution Rejected')
ORDER BY `id` DESC

Try this one sir:
DB::table('tickets')
->whereBetween('created_at',[$start, $end])
->orWhere(function($query)
{
$query->where('state', '!=', 'Resolved')
->where('state', '!=', 'closed')
->where('state', '!=', 'Cancelled')
->where('state', '!=', 'Solution Rejected')
->orderBy('id', 'desc');
})
->get();

Related

Laravel whereRelation() not filtring the query

I need to select all offers where the merchant has status open and dd_merchant set to 1. The match between merchant and offer is done using a col named mid.
In my offer model I have the following:
public function merchants()
{
return $this->belongsTo(Merchant::class,'mid','mid');
}
My query looks like this:
$data['offers']= Offer::whereRelation('merchants', 'status', 'Open')
->whereRelation('merchants', 'dd_merchant', '1')
->where('status', '=', 'Loaded')
->where("start", "<=", date("Y-m-d"))
->where("end", ">=", date("Y-m-d"))
->where('category', '=', $request->category)
->orderBy('title','ASC')
->paginate(20);
For the sake of this test, I have one merchant set to status 'Closed' and has dd_merchant set to 0 in the database.
The query returned by this is
select * from `offers` where (exists (select * from `merchants` where `offers`.`mid` = `merchants`.`mid` and `status` = 'Open' and `merchants`.`deleted_at` is null) and exists (select * from `merchants` where `offers`.`mid` = `merchants`.`mid` and `dd_merchant` = '1' and `merchants`.`deleted_at` is null) and `status` = 'Loaded' and `start` <= '2022-10-03' and `end` >= '2022-10-03' ) and `offers`.`deleted_at` is null order by `title` asc limit 20 offset 0
For some reason, I am still getting offers from that merchant with the above query. Am I assuming wrong that whereRelation() filters the offers based on the condition on the second table? If so, how can I achieve my goal?
In one of the 27 variants of this I tried so fare, I used the wherehas() like this:
$data['offers']= Offer::
whereHas('merchants', function (Builder $query) {
$query->where('dd_merchant', '=', '1')->where('status', '=', 'Open');
})
->where('status', '=', 'Loaded')
->where("start", "<=", date("Y-m-d"))
->where("end", ">=", date("Y-m-d"))
->where('category', '=', $request->category)
->orderBy('title','ASC')
->paginate(20);
The above made a lot more sense to me but I get the same result ... I get offers that should not be there.

Convert SQL query to Laravel way(unix timestamp problem!!)

I'm trying to conver this SQL query to Laravel query.
SELECT count(*) FROM (SELECT order_id FROM table1 WHERE app_process='7' AND ( service_type='lite' OR ((end_time-".time().")/86400)>'0') ) a INNER JOIN (SELECT order_id FROM table2 WHERE process='1' AND amount>'0' GROUP BY order_id) b ON a.order_id=b.order_id
I almost success(?) to converting but I don't know how to convert the time part.
end_time-".time().")/86400
what I converted
Db::table('table1 as A')
->select('A.order_id')
->where('A.app_process', '=', '7')
->where('A.service_type', '=', 'lite')
->orWhere('A.end_time', '>', '0') <== problem here!!
->join(Db::raw('(select order_id from table2 where process = 1 and amount > 0 group by order_id) B'), 'B.order_id', '=', 'A.order_id')
->count();
Could someone help me to solve the time part?
As far as I understand the query, it just checks if end_time is in the future. So you can just do the following:
->orWhere('A.end_time', '>', now())
now() is a Laravel helper that returns the current datetime.
I finally found out the right query. It turns out the problem was not only just 'time()' but also the wrong converted query.
I post this maybe help someone.
Db::table('table1 as A')
->leftJoin('table2 as B', 'A.order_id', '=', 'B.order_id')
->where('A.app_process', '=', '7')
->where(function($query){
$query->where('A.service_type', '=', 'lite')->orWhere('A.end_time', '>', time());
})
->where('B.process', '=', '1')
->where('B.amount', '>', '0')
->distinct()
->count('A.order_id');
try this I think it helps you
Db::table('table1 as A')
->select('A.order_id')
->where([['A.app_process', '=', '7'],['A.service_type', '=', 'lite']])
->orWhere('A.end_time', '>', now())
->join(Db::raw('(select order_id from table2 where process = 1 and amount > 0 group by order_id) B'), 'B.order_id', '=', 'A.order_id')
->count();

Composing complex Laravel eloquent where statement

I need to execute the following MySQL query using Laravel Eloquent ORM:
select * from `events`
where `app_id` = 1
and ((`startDate` < now() and endDate is null) or (`endDate` < now()))
order by `startDate` desc
My best try so far:
Event::where('app_id', '=', 1)
-> where(function ($query) {
$query -> where([['startDate', '<', Carbon::now() -> toDateTimeString()]) -> whereNull('endDate');
}) -> orWhere([['endDate', '<', Carbon::now() -> toDateTimeString()]])
-> orderBy('startDate', 'desc') -> get();
Which is not exactly what I want as it results in:
select * from `events`
where `app_id` = 1
and (`startDate` < now() and endDate is null) or (`endDate` < now())
order by `startDate` desc
(Missing extra parentheses in the second compound where condition).
Any idea? Thanks in advance.
This should do the task:
Event::where('app_id', '=', 1)
->where(function ($q) {
$q->where(function($query){
$query->where('startDate', '<', Carbon::now()->toDateTimeString())->whereNull('endDate');
})->orWhere('endDate', '<', Carbon::now()->toDateTimeString());
})
->orderBy('startDate', 'desc') -> get();

convert SQL query to query builder style

Im trying days to understand how I can convert a SQL query to a query builder style in laravel.
My SQL query is:
$tagid = Db::select("SELECT `id` FROM `wouter_blog_tags` WHERE `slug` = '".$this->param('slug')."'");
$blog = Db::select("SELECT *
FROM `wouter_blog_posts`
WHERE `published` IS NOT NULL
AND `published` = '1'
AND `published_at` IS NOT NULL
AND `published_at` < NOW()
AND (
SELECT count( * )
FROM `wouter_blog_tags`
INNER JOIN `wouter_blog_posts_tags` ON `wouter_blog_tags`.`id` = `wouter_blog_posts_tags`.`tags_id`
WHERE `wouter_blog_posts_tags`.`post_id` = `wouter_blog_posts`.`id`
AND `id`
IN (
'".$tagid[0]->id."'
)) >=1
ORDER BY `published_at` DESC
LIMIT 10
OFFSET 0");
Where I now end up to convert to the query builder is:
$test = Db::table('wouter_blog_posts')
->where('published', '=', 1)
->where('published', '=', 'IS NOT NULL')
->where('published_at', '=', 'IS NOT NULL')
->where('published_at', '<', 'NOW()')
->select(Db::raw('count(*) wouter_blog_tags'))
->join('wouter_blog_posts_tags', function($join)
{
$join->on('wouter_blog_tags.id', '=', 'wouter_blog_posts_tags.tags_id')
->on('wouter_blog_posts_tags.post_id', '=', 'wouter_blog_posts.id')
->whereIn('id', $tagid[0]->id);
})
->get();
I have read that I can't use whereIn in a join. The error i now get:
Call to undefined method Illuminate\Database\Query\JoinClause::whereIn()
I realy dont know how I can convert my SQL to query builder. I hope when I see a good working conversion of my query I can understand how I have to do it next time.
This work for me:
DB::table('wouter_blog_posts')
->whereNotNull('published')
->where('published', 1)
->whereNotNull('published_at')
->whereRaw('published_at < NOW()')
->whereRaw("(SELECT count(*)
FROM wouter_blog_tags
INNER JOIN wouter_blog_posts_tags ON wouter_blog_tags.id = wouter_blog_posts_tags.tags_id
WHERE wouter_blog_posts_tags.post_id = wouter_blog_posts.id
AND id
IN (
'".$tagid."'
)) >=1")
->orderBy('published_at', 'desc')
->skip(0)
->take(10)
->paginate($this->property('postsPerPage'));
The following Query Builder code will give you the exact SQL query you have within your DB::select:
DB::table('wouter_blog_posts')
->whereNotNull('published')
->where('published', 1)
->whereNotNull('published_at')
->whereRaw('`published_at` < NOW()')
->where(DB::raw('1'), '<=', function ($query) use ($tagid) {
$query->from('wouter_blog_tags')
->select('count(*)')
->join('wouter_blog_posts_tags', 'wouter_blog_tags.id', '=', 'wouter_blog_posts_tags.tags_id')
->whereRaw('`wouter_blog_posts_tags`.`post_id` = `wouter_blog_posts`.`id`')
->whereIn('id', [$tagid[0]->id]);
})
->orderBy('published_at', 'desc')
->skip(0)
->take(10)
->get();
The subquery condition had to be reversed because you can't have a subquery as the first parameter of the where method and still be able to bind the condition value. So it's 1 <= (subquery) which is equivalent to (subquery) >= 1. The query generated by the above code will look like this:
SELECT *
FROM `wouter_blog_posts`
WHERE `published` IS NOT NULL
AND `published` = 1
AND `published_at` IS NOT NULL
AND `published_at` < Now()
AND 1 <= (SELECT `count(*)`
FROM `wouter_blog_tags`
INNER JOIN `wouter_blog_posts_tags`
ON `wouter_blog_tags`.`id` =
`wouter_blog_posts_tags`.`tags_id`
WHERE `wouter_blog_posts_tags`.`post_id` =
`wouter_blog_posts`.`id`
AND `id` IN ( ? ))
ORDER BY `published_at` DESC
LIMIT 10 offset 0
My process when creating more complex queries is to first create them and try them out in a SQL environment to make sure they work as indended. Then I implement them step by step with the Query Builder, but instead of using get() at the end of the query, I use toSql() which will give me a string representation of the query that will be generated by the Query Builder, allowing me to compare that to my original query to make sure it's the same.

Why do Eloquent has and whereHas methods use subqueries instead of joins?

Why does Laravel use subqueries for the Eloquent has and whereHas methods instead of using joins? For example, I have constructed an Eloquent query like this:
Order::whereHas('orderProducts', function($query)
{
$query->where('product_id', $this->id);
})
->whereHas('status', function($query)
{
$query->where('sales_data', 1);
})
->where('ext_created_at', '>=', $startDate)
->where('ext_created_at', '<=', $endDate.' 23:59:59')
->distinct()
->count('id');
The resulting query is:
select count(distinct `id`) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
This query returns a result of 16. According to DB::getQueryLog(), this takes 23222.14 ms to execute.
I then constructed another query like this:
DB::table('orders')
->leftJoin('order_products', 'orders.id', '=', 'order_products.order_id')
->leftJoin('order_statuses', 'orders.order_status_id', '=', 'order_statuses.id')
->whereNull('orders.deleted_at')
->where('order_products.product_id', $this->id)
->where('orders.ext_created_at', '>=', $startDate)
->where('orders.ext_created_at', '<=', $endDate.' 23:59:59')
->whereNull('order_products.deleted_at')
->where('order_statuses.sales_data', 1)
->distinct()
->count('orders.id')
The resulting query is:
select count(distinct `orders`.`id`) as aggregate from `orders` left join `order_products` on `orders`.`id` = `order_products`.`order_id` left join `order_statuses` on `orders`.`order_status_id` = `order_statuses`.`id` where `orders`.`deleted_at` is null and `order_products`.`product_id` = ? and `orders`.`ext_created_at` >= ? and `orders`.`ext_created_at` <= ? and `order_products`.`deleted_at` is null and `order_statuses`.`sales_data` = ?
This query also returns 16, but takes 3.52 ms to run. Obviously, using joins has much better performance here, but is that only specific to my use case, or are joins generally better performance? If so, why wouldn't Eloquent use joins over subqueries? I would much rather work with models and make use of all the excellent Eloquent methods like has and whereHas, but the performance is so dramatically different that I am being forced to construct the query myself using the Query Builder.
Am I missing something here?
I am using Laravel Framework version 4.2.16.

Categories