orWhere with multiple conditions in Eloquent - mysql

I am looking to write the following query in eloquent:
select * from stocks where (symbol like '%$str%' AND symbol != '$str' ) OR name like '$str%'
Without the last condition, it's simple:
$stocks = Stock::live()
->where('symbol','like','%'.$str.'%')
->where('symbol','!=',$str)
->get();
But adding orWhere('name','like',$str.'%') after the two wheres returns incorrect results. Basically I am wondering how to emulate what I accomplished by using the (condition1 AND condition2) OR condition3 syntax in the raw query above.

Try
$stocks = Stock::live()
->where('name', 'like' , '%'.$str.'%')
->orWhere(function($query) use($str) {
$query->where('symbol','like','%'.$str.'%')
->where('symbol','!=',$str); // corrected syntax
})->get();

Try
$stocks = Stock::live()->where('name', 'like' , '%'.$str.'%')
->orWhere(function($query) use($str) {
$query->where('symbol','like','%'.$str.'%')
->where('symbol','!=',$str)
})->get();

I didn't test this, so sorry if it doesn't work. But I think one of these solutions could work.
$stocks = Stock::live()
->where([
['symbol','like','%'.$str.'%'],
['symbol', '!=', $str],
])
->orWhere('name','like', $str.'%')
->get();
and
->where(function ($query) use ($str) {
$query->where([
['symbol','like','%'.$str.'%'],
['symbol', '!=', $str],
]);
})
->orWhere(function ($query) use ($str) {
$query->where('name','like', $str.'%');
});

Related

How to use orWhere() optimally in the query?

Here is my query:
$notifications = \App\Notification::query()->where('users_scope', 'customer')
->with('userSeen')
->where(function ($query) use ($user) {
$query->where('user_id_to', 20288)
->orWhere(function($q) use ($user) {
$q->Where('user_id_to', null)
->Where(function($q) use ($user) {
$q->where('expire_at', null)->where('created_at', '>=', "2020-04-03 04:18:42");
$q->orWhere('expire_at', '!=', null)->where('expire_at', '>', Carbon::now());
});
});
})
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])->orderBy('id', 'desc')->limit(20)->get();
Also here is the relation (used as with()):
public function userSeen()
{
return $this->belongsToMany(User::class, NotificationSeen::class, 'notification_id', 'user_id');
}
Here is the scenario: Query above gets the last 20 user's notifications (latest notifications list). Recently, the size of the notifications table is increased too much. It has over 8 million rows at the moment. And the query takes over 10 seconds to be executed.
Noted that, all needed indexes have been created as well on both tables (notifications and userSeen tables). Also, that relation (userSeen) is like a pivot table that indicates the user either has seen the notification or not.
Any idea how can I rewrite that query to be more optimal?
Explanations about the logic:
20288 is hardcoded and will be $user->id in reality.
when user_id_to is null, it means it's a bulk notification (must be visible for all users)
User can see bulk notifications if they have a bigger created_at value than the user's created_at.
Sometimes bulk notifications has an expire time (like marketing campaigns) that must be shown to the users if still not outdated.
Bases on the logic you mentioned in your question, this should get you what you need, however, it probably won't help that much with the speed:
$notifications = \App\Notification::query()
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])
->with('userSeen')
->where('users_scope', 'customer')
->where(function ($query) use ($user) {
$query
->where('user_id_to', $user->id)
->orWhere(function ($query) use ($user) {
$query
->whereNull('user_id_to')
->where('created_at', '>=', $user->created_at)
->where(function ($query) {
$query->whereNull('expire_at')->orWhere('expire_at', '>=', now());
});
});
})
->orderByDesc('id')
->limit(20)
->get();
I would also suggest trying 2 separate queries instead of using SQL_CALC_FOUND_ROWS. Here are a couple of SO posts that explain why:
Which is fastest? SELECT SQL_CALC_FOUND_ROWS FROM `table`, or SELECT COUNT(*)
SELECT SQL_CALC_FOUND_ROWS Query very slow greater than 250000 records
You queries would then look something like:
$query = \App\Notification::query()
->with('userSeen')
->where('users_scope', 'customer')
->where(function ($query) use ($user) {
$query
->where('user_id_to', $user->id)
->orWhere(function ($query) use ($user) {
$query
->whereNull('user_id_to')
->where('created_at', '>=', $user->created_at)
->where(function ($query) {
$query->whereNull('expire_at')->orWhere('expire_at', '>=', now());
});
});
})
->orderByDesc('id');
$count = $query->count();
$notifications = $query->limit(20)->get();
Alternatively, you could use something like paginate().
I am not sure what condition you have but condition you can apply like below
$notifications = \App\Notification::query()->where('users_scope', 'customer')
->with('userSeen')
->where(function ($query) use ($user) {
$query->where('user_id_to', 20288);
if($user){
$query->orWhere(function($q) use ($user) {
$q->Where('user_id_to', null)
->Where(function($q) use ($user) {
$q->where('expire_at', null)->where('created_at', '>=', "2020-04-03 04:18:42");
$q->orWhere('expire_at', '!=', null)->where('expire_at', '>', Carbon::now());
});
});
}
})
->select([DB::raw('SQL_CALC_FOUND_ROWS *')])->orderBy('id', 'desc')->limit(20)->get();
Even you can use when clause
Ref:https://laravel.com/docs/8.x/queries#conditional-clauses

SQL Laravel Eloquent - Where field can be uppercase or lowercase

$pname can be "Airbus", "airbus" or "AIRBUS"
$info = Plane::where('plane_name', '=', $pname)
->where('uid', '=', $uid)
->get();
Is there any way I can update this query to check the database for the plane name without case sensitivity?
Use LOWER MYsql function and strtolower php function
$info = Plane::whereRaw('LOWER(plane_name) = (?)', [strtolower($pname)])
->where('uid', '=', $uid)
->get();
OR
Plane::where('plane_name', 'ilike', $pname)->where('uid', '=', $uid)
->get();
just make sure result where clause to lowercase and query clause to lowercase too.
where(DB::raw('lower(column_name)'), '=', Str::lower($query))
dont forget to use:
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
Add -> use Illuminate\Support\Facades\DB; in Controller file.
-----------Example---------------
UserInfo::where(DB::raw('upper(username)'), strtoupper($Request->username))
It's work for me, hope to help you.
To solve my issue, I have to do that on my Model.
simple copy and paste this function
public function __get($key)
{
if (is_null($this->getAttribute($key))) {
return $this->getAttribute(strtoupper($key));
} else {
return $this->getAttribute($key);
}
}

how to make if statement in the laravel query builder

I have the following SQL query
$query
->join('cities','tickets.city_id','=','cities.id')
->select(
'tickets.id',
'tickets.biker_id',
'tickets.picked_up',
'tickets.delivered',
'tickets.service_charge',
'tickets.amount',
'tickets.cancelled',
'tickets.pre_order',
'tickets.created_by',
'tickets.created_at'
)
->whereDay('tickets.created_at',Date('d'))
->orderBy('tickets.created_at','desc')
->get();
my aim is to set the
whereday('tickets.created_at', Date('d'))
to
whereday('tickets.created_at', Date('d', strtotime("-1 day")))
whenever the tickets.pre_order = 1
but when tickets.pre_order = 0 i will stick to the
whereday('tickets.created_at',Date('d'))
is it possible using if statement or is there any better way to solve this?
make it like this
->where(function ($query){
$query->where('tickets.created_at', Carbon::now()->subDays()->format('d'))
->where('tickets.pre_order',1);
})->orWhere(function ($query){ $query->where('tickets.created_at',
Carbon::now()->format('d')) ->where('tickets.pre_order',0); })
->get();
To subtract a day and format it, use Carbon library for DateTime in PHP (as given in comments by #spartyboy )
$query
->join('cities','tickets.city_id','=','cities.id')
->select(
'tickets.id',
'tickets.biker_id',
'tickets.picked_up',
'tickets.delivered',
'tickets.service_charge',
'tickets.amount',
'tickets.cancelled',
'tickets.pre_order',
'tickets.created_by',
'tickets.created_at'
)
->where(function ($query) {
$query
->where('tickets.pre_order', 0)
->whereDay('tickets.created_at', Date('d'));
})
->orWhere(function ($query) {
$query
->where('tickets.pre_order', 1)
->whereDay('tickets.created_at', Carbon::yesterday()->format('d'));
})
->orderBy('tickets.created_at','desc')
->get();
or if you want to subtract multiple days then
instead of yesterday() use now()->subDays($days_count)

Not getting proper result but working in sql

This is the code that is already working in mysql database but I want to convert it into Laravel 5.6
SELECT *
FROM `listings`
WHERE (
country_id=1
AND (state=32 or city=8)
AND (listing_id LIKE "%6562%" OR title LIKE "%6562%"))
Supposedly you have a model called Listing which takes care of the listings table. You can write the query like this:
$listings = App\Listing::where('field1', 1)
->where(function ($query) {
$query->where('field2', 32);
$query->orWhere('field3', 8);
})
->where(function ($query) {
$query->where('field4', 'LIKE', '%6562%');
$query->orWhere('field5', 'LIKE', '%6562%');
})
->get();
The first parameter of the where method can be a callback which can achieve this type of grouping (field2=32 or field3=8)

How to use OR inside WHERE in laravel Query

I'm new to laravel and I need help for query in laravel
My Custom Query
$sql1="SELECT * FROM blogs
WHERE
('$title'='' OR title='$title')
AND
('$body'='' OR body='$body')";
and i create a laravel build query but don't know how to put OR inside WHERE and Put Brackets
$posts = Blog::where('title','LIKE',"%{$title}%")
->Where('body', 'LIKE',"%{$body}%")
->offset($start)
->limit($limit)
->orderBy($order,$dir)
->get();
I think orWhere is what you're looking for and based off custom query I think you would need = instead of like unless thats what youre going for, I think it would be something like:
->where('title', '=', $title)
->orWhere('title', '=', '');
$posts = Blog::where(function($q) use($title){
$q->where('title','')->orWhere('title',$title);
})->where(function($q) use($body){
$q->where('body','')->orWhere('body',$body);
})->offset($start)
->limit($limit)
->orderBy($order,$dir)
->get();
Just use orWhere()
$posts = Blog::where('title', $title)
->orWhere('title', $otherTitle)
->orderBy($order, $dir)
->get();
Your query should be like :
$result = Blog::where(function($query) use ($title, $body){
$query->where(
["title" => $title, "body"=>$body]
)->orWhere(
["title"=>"", "body"=>""]
);
})
->offset($start)
->limit($limit)
->orderBy($order,$dir)
->get();
Hope this give you an idea :)
$posts = Blog::where(function ($query) use($title) {
$query->where('title', 'LIKE', "%{$title}%")
->orWhere('title', '=', '$title');
})
->orWhere(function ($query) use($body) {
$query->where('body', 'LIKE', "%{$body}%")
->orWhere('body', '=', '$body');
})
->offset($start)
->limit($limit)
->orderBy($order,$dir)
->get();
Not tested...
Use the ->orWhere() code.
Note: You can also make use of magic methods
->whereAgeOrPhone(18, 123456789);
use orWhere()
$blogs= DB::table('Blog')
->where('title', 'LIKE', '%{$title}%')
->orWhere('body','LIKE', '%{$body}%')
->offset($start)
->limit($limit)
->orderBy($order,$dir)
->get();
For More Query, You can visit here