Multiple Where Clause in Laravel not working as needed - laravel

I am trying to get all the expired listings from the db. the table has three fields for the expiry date, to_date_1, to_date_2, to_date_3.
date 2&3 may have a null value but date 1 always has a value. If date 3 is not null I want the query to compare with date 3 and skip date1 &2. if date 3 is null, the query should compare with date 2, otherwise date 1 will be the expiry date. here is my query.
$query = Listing::query()
->where('vendor_id', $vendor->id)
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->orWhere('to_date_1', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orderBy('created_at', 'desc');
$listings = $query->paginate(10);
Here is a sample data:
id
from_date_1
to_date_1
from_date_2
to_date_2
from_date_3
to_date_3
1
2021-06-10
2021-08-15
2021-08-16
2021-08-31
2021-09-01
2021-09-15
2
2021-06-25
2021-08-10
2021-08-11
2021-08-22
NULL
NULL
3
2021-06-25
2021-08-20
NULL
NULL
NULL
NULL
if today date is: 21-08-2021, the result should be record: 1 & 2

As Pavlo said group your query inside a where
$query = Listing::query()
->where(function ($query) { // Wrap you code inside a where
$query->where('vendor_id', $vendor->id)
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', Carbon::now()->format('Y-m-d'));
})
->where(function ($q) {
return $q->orWhere('to_date_1', '<', Carbon::now()->format('Y-m-d'));
});
})
->orderBy('created_at', 'desc');
$listings = $query->paginate(10);

Try use orWhere
->where('is_deleted', 0)->where('is_published', 1)->where('is_approved', 1)
->where(function ($q) {
return $q->WhereNotNull('to_date_3')->Where('to_date_3', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orWhere(function ($q) {
return $q->WhereNotNull('to_date_2')->Where('to_date_2', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
->orWhere(function ($q) {
return $q->orWhere('to_date_1', '<', \Carbon\Carbon::now()->format('Y-m-d'));
})
More about parameters grouping
UPDATING (where -> orWhere -> orWhere give you your sql)

Related

Laravel slow query for 30000 db row

I have 32000 occurrence of transactions in my db, this query takes too long
about 10 sec or more,
how can i optimize please
$data = DB::table('transactions')
->when($merchantId, function($query) use($merchantId) {
return $query->where('user_id', $merchantId);
})
->whereBetween('created_at', [
Carbon::now()->startOfYear(),
Carbon::now()->endOfYear(),
])->when($app, function($query) use($app) {
return $query->where('application_id', $app);
})
->where(function ($query) {
$query->where('status', 'CANCELLED')
->orWhere('status', 'FAILED');
})
->selectRaw("count(*) as nombre, extract($period from transactions.created_at) as $period")
->groupBy("$period")
->pluck('nombre', "$period");

Join head table with position table with laravel eloquent

I have the following construction:
(Laravel Version 5.4)
I have a head table (series), and a table with associated positions (posts). The positionstable has three values whose determine, whether the posts should be displayed, or even not. The series table has this information as well.
These columns are: 'published' (boolean), 'published_from' (datetime) and 'published_to' (datetime).
To display the posts, the series and the posts, had to be published (published = 1), and the actual date/time has to be grater, or euqal, than the published_from value, of the series and the post, and the actual date/time has to be lower or equal than the published_to value, as well.
To chek whether the postion should be displayed, I wrote a eloquent scope:
public function scopePublishedRestriction($query)
{
$date = Carbon::today();
$today = date('Y-m-d', $date->getTimestamp());
$query->accessible()
->join('learning_series', function ($join) use ($today) {
$join->on($this->table . '.learning_serie_id', 'learning_series.id')
->where('learning_series.published', 1)
->whereDate('learning_series.published_from', '<=', $today)
->where(function ($join) use ($today) {
$join->orWhere(function ($join) use ($today) {
$join->whereDate('learning_series.published_to', '>=', $today);
})
->orWhere('learning_series.published_to', '0000-00-00 00:00:00');
});
})
->where($this->table . '.published', 1)
->where($this->table . '.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate($this->table . '.published_to', '>=', $today);
})
->orWhere($this->table . '.published_to', '0000-00-00 00:00:00');
});
return $query;
}
The created SQL-statement is correct,
select * from `brshop_docs_learning_posts`
inner join `brshop_docs_learning_series` on `brshop_docs_learning_posts`.`learning_serie_id` = `brshop_docs_learning_series`.`id`
and `brshop_docs_learning_series`.`published` = ?
and date(`brshop_docs_learning_series`.`published_from`) <= ?
and ((date(`brshop_docs_learning_series`.`published_to`) >= ?) or `brshop_docs_learning_series`.`published_to` = ?)
where `brshop_docs_learning_posts`.`visible` = ?
and `brshop_docs_learning_posts`.`active` = ?
and `brshop_docs_learning_posts`.`published` = ?
and `brshop_docs_learning_posts`.`published_from` <= ?
and ((date(`brshop_docs_learning_posts`.`published_to`) >= ?) or `brshop_docs_learning_posts`.`published_to` = ?)
and `brshop_docs_learning_posts`.`deleted_at` is null
I think, the merge of the collections is wrong. For example, every post in this collection has the ID of the series. So every column, that exists in the series table and in the posts table has the value of the series table.
Thank you for your help ;-)
Markus
I solved it with whereHas()
public function scopePublishedRestriction($query)
{
$date = Carbon::today();
$today = date('Y-m-d', $date->getTimestamp());
$query->accessible()
->whereHas('serie', function($query) use($today){
$query->where('learning_series.published', 1)
->whereDate('learning_series.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate('learning_series.published_to', '>=', $today);
})
->orWhere('learning_series.published_to', '0000-00-00 00:00:00');
});
})
->where($this->table . '.published', 1)
->where($this->table . '.published_from', '<=', $today)
->where(function ($query) use ($today) {
$query->orWhere(function ($query) use ($today) {
$query->whereDate($this->table . '.published_to', '>=', $today);
})
->orWhere($this->table . '.published_to', '0000-00-00 00:00:00');
});
return $query;
}

Laravel query with relationships and conditions

Hi I have 3 tables in DB. Here is my query:
$nav = PageNav::with([
'page' => function($query) {
$query->where('status', 1)
->where(function($query) {
$query->whereNull('publish_up')
->whereNull('publish_down');
})
->orWhere(function ($query) {
$query->where('publish_up', '<', date('Y-m-d H:i:s'))
->where('publish_down', '>', date('Y-m-d H:i:s'));
});
},
'langs' => function ($query) use ($lang) {$query->where('language_code', '=', $lang);},
])
->get();
It generates this queries:
When I loop $nav there are still pages with status = 0. I really don't understand why? Any suggestions?
As with any SQL query or Eloquent query, OR completely separates the conditions, so as soon as you have an orWhere in eloquent, it will match anything that matches before the OR or after the OR.
You can resolve this by using a nested where, or parentheses in SQL, to properly encapsulate the conditions.
$query->where('status', 1)
->whereNested(function($q1) {
$q1->where(function($query) {
$query->whereNull('publish_up')
->whereNull('publish_down');
})
->orWhere(function ($query) {
$query->where('publish_up', '<', date('Y-m-d H:i:s'))
->where('publish_down', '>', date('Y-m-d H:i:s'));
});
});
Ok I found solution. Needed to wrap condition to whereHas
$nav = PageNav::whereHas('page', function ($query) {
$query->where('status', 1)
->whereNested(function($q1) {
$q1->where(function($query) {
$query->whereNull('publish_up')
->whereNull('publish_down');
})
->orWhere(function ($query) {
$query->where('publish_up', '<', date('Y-m-d H:i:s'))
->where('publish_down', '>', date('Y-m-d H:i:s'));
});
});
})
->with([
'page',
'langs' => function ($query) use ($lang) {
$query->where('language_code', '=', $lang);
}
])
->get();

Laravel query builder not replacing question mark

I'm running the following query:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', Project::STATUS_ARCHIVED);
})
->select('tasks.id');
});
But if I output the whole raw query I get the following:
select * from `tasks` where `tasks`.`company` = 1 and `id` not in (select `tasks`.`id` from `tasks` inner join `projects` on `projects`.`id` = `tasks`.`project` and `projects`.`status` = ?)
As you can see at the end of the raw query there's a question mark that wasn't replaced with the actual value, instead 'tasks'.'company' = 1 was.
You can listen to the illuminate.query event. Before the query add the following event listener:
use Event;
Event::listen('illuminate.query', function($query, $params, $time)
{
dd([
$query, // prepared statement
$params, // query params (? symbols will be replaced with)
$time // execution time
]);
});
I found a solution to this issue by manually setting the bindings using
->setBindings([Project::STATUS_ARCHIVED]);
Here's the whole snippet:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', '?');
})
->select('tasks.id')
->setBindings([Project::STATUS_ARCHIVED]);
})
->where('status', '=', Task::STATUS_INCOMPLETE);
You didn't add ->get(); to the end of the query.
Try:
return $this->hasMany('App\Task', 'company')
->whereNotIn('id', function($query)
{
$query->from('tasks')->join('projects', function($join)
{
$join->on('projects.id', '=', 'tasks.project')
->where('projects.status', '=', Project::STATUS_ARCHIVED);
})
->select('tasks.id');
})->get();

Laravel 4 advanced wheres

I have my query working perfectly in mySQL, however am struggling to use the advanced wheres inside the laravel query builder.
Can anyone assist in converting this at all?
So far I have:
LARAVEL
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events= DB::table('events')
->where('hotel_id', 4)
->orWhere(function($query)
{
$query->where($start_request, '<=', 'start_time')
->where($end_request, '>', 'start_time');
})
->orWhere(function($query)
{
$query->where($start_request, '>=', 'start_time')
->where($end_request, '<', 'end_time');
})
->get();
mySQL
SELECT id, title, description, start_time, end_time FROM events where hotel_id = 4 and (('2014-12-18 09:00' <= start_time and '2014-12-18 10:00' > start_time)
or ('2014-12-18 09:00' >= start_time and '2014-12-18 10:00' < end_time));
Can you try this?
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events= DB::table('events')
->where('hotel_id', 4)
->where(function($query) use($start_request, $end_request) {
$query->where(function($subquery) use($start_request, $end_request){
$subquery->where('start_time', '>=', $start_request)
->where('start_time', '<', $end_request);
});
$query->orWhere(function($subquery1) use($start_request, $end_request) {
$subquery1->where('start_time', '<=', $start_request)
->where('end_time', '>', $end_request);
});
})
->get();
Something like this should do the job:
$start_request = '2014-12-18 09:00';
$end_request = '2014-12-18 10:00';
$events = DB::table('events')
->where('hotel_id', 4)
->where(function ($q) use ($start_request, $end_request) {
$q->where(function ($query) use ($start_request, $end_request) {
$query->where($start_request, '<=', 'start_time')
->where($end_request, '>', 'start_time');
})
->orWhere(function ($query) use ($start_request, $end_request) {
$query->where($start_request, '>=', 'start_time')
->where($end_request, '<', 'end_time');
});
})
->select('id', 'title', 'description', 'start_time', 'end_time')
->get();

Resources