Multiple where conditions with 'AND' & 'OR' operators in laravel - php

in my laravel controller i'm trying to perform the following query for the user search.
$data = User::orderBy('id','DESC')
->where('email','LIKE','%'.$request->search."%")
->orwhere('first_name','LIKE','%'.$request->search."%")
->orwhere('last_name','LIKE','%'.$request->search."%")
->WHERE('role_id','=','3')
->paginate(12);
return view('admins.participants.results',compact('data'))
->with('i', ($request->input('page', 1) - 1) * 12 );
What I really want to perform is,
SELECT * FROM USERS WHERE role_id='3' AND email LIKE '%search_string%' OR first_name LIKE '%search_string%' OR last_name LIKE '%search_string%' ;
But the above laravel query outputs all the users without considering the `role_id=3

You can use where callback
$data = User::orderBy('id','DESC')
->where('role_id',3)
->where(function($query)use($request){
$query->where('email','LIKE','%'.$request->search."%");
$query->orwhere('first_name','LIKE','%'.$request->search."%");
$query->orwhere('last_name','LIKE','%'.$request->search."%");
})->paginate(12);
also you can change
'%'.$request->search."%"
to
"%{$request->search}%"

You need to pass OR clauses to a Closure to group them, otherwise the query doesn't really understand which clauses are optional and which are not.
$data = User::orderBy('id','DESC')
->where('role_id','=','3')
->where(function($query) use ($request) {
$query->where('email','LIKE','%'.$request->search."%")
->orwhere('first_name','LIKE','%'.$request->search."%")
->orwhere('last_name','LIKE','%'.$request->search."%");
})
->paginate(12);

Related

Is there any way to get two different results with single query based on condition?

I am trying to get two different results from single query but the problem is both conditions get applied on last query.
for example
$getUser = User::join('role_user','role_user.user_id','users.id')
->select('users.id','users.name','users.email','users.identity_status','users.email_verified_at','role_user.role_id')
->where('role_user.role_id',2)
->whereNotNull('users.email_verified_at');
$newMailUsers = $getUser->where('new_mail',0)->get();
$topSellingMailUsers = $getUser->where('topselling_mail',0)->get();
but when i checked sql query of $topSellingMailUsers i saw that both the conditions of new_mail and topselling_mail applied in $topSellingMailUsers query what i want is it should not consider mail condition in query.
how can i get two different results of $newMailUsers, $topSellingMailUsers based on both conditions separately.
Every time you use where() method you are mutating $getUser query builder.
You can chain your query builder with clone() method and this will return another query builder with the same properties.
$getUser = User::join('role_user','role_user.user_id','users.id')
->select('users.id','users.name','users.email','users.identity_status','users.email_verified_at','role_user.role_id')
->where('role_user.role_id',2)
->whereNotNull('users.email_verified_at');
$newMailUsers = $getUser->clone()->where('new_mail',0)->get();
$topSellingMailUsers = $getUser->clone()->where('topselling_mail',0)->get();
you need to clone Builder object to reuse it
$selectFields = [
'users.id',
'users.name',
'users.email',
'users.identity_status',
'users.email_verified_at',
'role_user.role_id'
];
/** #var Illuminate\Database\Eloquent\Builder */
$getUser = User::join('role_user', 'role_user.user_id', 'users.id')
->select($selectFields)
->where('role_user.role_id', 2)
->whereNotNull('users.email_verified_at');
$newMailUsers = (clone $getUser)->where('new_mail', 0)->get();
$topSellingMailUsers = (clone $getUser)->where('topselling_mail', 0)->get();
Well, if you have relations set up on models will be easier, but still can do it:
// Let's say you pass sometimes the role_id
$role_id = $request->role_id ?? null;
$getUser = User::join('role_user','role_user.user_id','users.id')
->select('users.id','users.name','users.email','users.identity_status','users.email_verified_at','role_user.role_id')
// ->where('role_user.role_id',2) // replaced with ->when() method
->when($role_id, function ($query) use ($role_id){
$query->where('role_user.role_id', $role_id);
})
->whereNotNull('users.email_verified_at');
This way it will return users with ALL ROLES or if ->when(condition is TRUE) it will return users with the role id = $role_id.
You can use multiple ->when(condition, function(){});
Have fun!
Instead of firing multiple queries you can do it in a single query as well by using collection method as like below.
$users = User::join('role_user','role_user.user_id','users.id')
->select('users.id','users.name','users.email','users.identity_status','users.email_verified_at','role_user.role_id', 'new_mail', 'topselling_mail')
->where('role_user.role_id',2)
->where(function($query) {
$query->where('new_mail', 0)->orWhere('topselling_mail', 0);
})
->whereNotNull('users.email_verified_at')
->get();
$newMailUsers = $users->where('new_mail',0)->get();
$topSellingMailUsers = $user->where('topselling_mail',0)->get();

Laravel where :: model, how to add multi statement

So how i can add multi statement to query in ::where model.
Actually it lok like that:
certs::where('unique', '=', $newUnique )->get()
i need somethig like that:
certs::where('unique, num', '=', $newUnique,$key )->get()
so in sql look like that
Select * From certs Where unique = $newUnique AND num = $key
I need that to check if data need to be updated or inserted.
Just append another where clause
certs::where('unique', $newUnique)->where('num', $key)->get()
For example
App\User::where('first_name', 'John')->where('last_name', 'Doe')->toSql();
Would result in
"select * from `users` where `first_name` = ? and `last_name` = ?"
Hope this helps
It can become tricky if there will be OR instead of AND, so you should always prefer
for AND
Certs::where(function($query) use ([$newUnique, $key]){
$query->where('first_name', 'John')->where('last_name', 'Doe');
})->get();
for OR
Certs::where(function($query) use ([$newUnique, $key]){
$query->where('first_name', 'John')->orWhere('last_name', 'Doe');
})->get();
Laravel allows multi statements for "where" function of Model by passing array of conditions.
The below code can be used if you are applying "and" condition and "=" operation for each data.
$conditions = ['unique'=>$newUnique,'num'=>$key];
Certs::where($conditions)->get();
Else if you want to use different operations with "and" condition, use below code.
$conditions = [
['unique','=',$newUnique],['num','!=',$key]
];
Certs::where($conditions)->get();
Also, you can use below code for "and" and "or" conditions.
Certs::where(function($query) use ($newUnique, $key]){
$query->where('unique', $newUnique);
$query->orWhere('num', $key);
})->get();
Similarly, you can use orWhere(), whereColumn(), whereBetween() and other query builder functions.
You can do something like this:
certs::where(['unique'=>$newUnique, 'num' => $key])->get()

Laravel - how to surround all previous clauses with where clause on builder ?

In database table I have one row :
users
id|email|is_deleted
1|test#test.com|1
I have this code :
User::where('email', 'test#test.com')
->orWhere('email', 'test2#test2.com')
->get();
and this query is generated :
select * from users where email = 'admin#myzone.com' or email = 'asdasdas'
with one result. Now I want apply where is_deleted = 0
If I do like this :
User::where('email', 'test#test.com')
->orWhere('email', 'test2#test2.com')
->where('is_deleted', 0)
->get();
Generated query is :
select * from "users" where "email" = ? or "email" = ? and "users"."deleted_at" is null
So far everything works as expected, this query returns one result, but I want only not deleted users, I can do following :
User::where(function($query){
$query->where('email', 'test#test.com')
->orWhere('email', 'test2#test2.com')
})->where('is_deleted', 0)
->get();
and this will work, but in my code I already have returned builder :
function applyNotDeleted(Builder $builder){
//here I want to filter only not deleted users,
//but this is already triggered on builder $query->where('email', 'test#test.com')->orWhere('email', 'test2#test2.com')
//currently generated query on builder is select * from users where email = 'admin#myzone.com' or email = 'asdasdas'
//but at this stage I want to create query which will look like select * from "users" where "email" = ? or "email" = ? and "users"."deleted_at" is null
//something like this
$builderNew = $builderNew->where(function($query){
$query->applyAllLogicFromCurrentBuilder($builder)
})
->where('is_deleted', 0)
->get();
}
any idea?
Personally I would use query scope to just obtain non deleted records
public function scopeNotDeleted(Builder $query): Builder
{
return $query->where('is_deleted', 0);
}
and then use it when fetching records
User::notDeleted()->where(function(Builder $query) {
$query->where('email', 'test#test.com')->orWhere('email', 'test2#test2.com');
})->get();
If you are using applyAllLogicFromCurrentBuilder method, you can also extract it to the query scope and just chain it with your call like so:
User::allLogicFromCurrentBuilder()->notDeleted()->where(function(Builder $query) {
$query->where('email', 'test#test.com')->orWhere('email', 'test2#test2.com');
})->get();
You can also keep your applyNotDeleted method as is, but without the call to get() method - this way you can append any further statements to it if need to. I would probably convert it to a public static method so you can call it without instantiating model:
User::applyNotDeleted(User::where(function(Builder $query) use ($email) {
$query->where('email', 'test#test.com')->orWhere('email', 'test2#test2.com');
}))->get();
Personally I would prefer the scope approach as it seems a bit cleaner.

how to use like operator in array laravel 5.2?

I want to add Like operator in array but it gives me error:
Unknown column '0' in 'where clause (admin_id = 2 and 0 = name and Like = %zdgbdsh%) order by id asc limit 10 offset 0)
Here is my query:-
$conditions = array();
if(!empty($data['name'])) {
$conditions = array_merge($conditions,array('name','Like'=>'%'.$data['name'].'%'));
}
And here is my final query:-
$querys = DB::table('users')
->where($conditions)
->skip($iDisplayStart)->take($iDisplayLength)
->OrderBy($orderby,$dir)
->get();
Note: I don't want directly in query like
where('users.name','like','%'.$data['name'].'%');**
I want to do in conditions variable
I am using laravel framework 5.2
It should be:
$conditions=array_merge($conditions,array('name' => 'Like %'.$data['name'].'%'));
Update
I've just realised that will produce name = Like %data%
You can change it to:
$conditions=array_merge($conditions,array('name', '%'.$data['name'].'%'));
and in where use:
->where(join(' LIKE ', $conditions))
This is not the greatest way of applying multiple conditions, But you can always user the whereRaw
so loop through a array with your conditions eg.
$conditions = [];
if (!empty($data['name'])) {
$conditions = array_merge(
$conditions,
["name LIKE '%".$data['name']."%'"]
);
}
$query = DB::table('users');
// apply add raw conditions
foreach($conditions as $condition){ $query->whereRaw($condition); }
$query->skip($iDisplayStart)->take($iDisplayLength)
->OrderBy($orderby,$dir)
->get();
you could also find some packages that will help you out with things like this.
Hope it helps

laravel convert to string mysql

I have this code written in using the laravel and I wonder how to write it in pure SQL without using laravel, as would be?
Route::get('popular', function(){
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
$data = array(
'media' => $media,
'categories' => Category::all(),
'pages' => Page::all(),
'settings' => Setting::first(),
);
return View::make('Theme::home', $data);
});
Using toSql method
Laravel Query builder has a helpful method called toSql which is handy to see what the final SQL query looks like.
Now, when you call the paginate method, you'll get a Paginator and you won't be able to call the toSql method.
Deconstructing paginate
When you use paginate, Laravel will make the following query for each $page:
// Let's simplify the query for now
Media::skip(($page - 1) * $perPage)->take($perPage);
Knowing that, you can use the query builder's method toSql and you'll see the actual sql string.
Supossing $page=3, $perPage=30, and that the name of the table is media, you'll get something like this:
Media::skip(2 * 30)->take(30)->toSql();
select * from media limit 30 offset 60
Now, for your actual query, you can use the following to see the resulting SQL string for the page 3 (as an example):
Media::where('active', '=', '1')
->join('media_likes', 'media.id', '=', 'media_likes.media_id')
->groupBy('media_likes.media_id')
->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')
->select('media.*')
->skip(2 * 30)
->take(30)
->toSql();
Listening For Query Events
Alternatively, you can set an event listener in your AppServiceProvider, and log each SQL query the application executes.
public function boot()
{
\DB::listen(function ($query) {
\Log::debug($query->sql);
\Log::debug($query->bindings);
\Log::debug($query->time);
});
}
$media = Media::where('active', '=', '1')->join('media_likes', 'media.id', '=', 'media_likes.media_id')->groupBy('media_likes.media_id')->orderBy(DB::raw('COUNT(media_likes.id)'), 'DESC')->select('media.*')->paginate(30);
Something like this:
SELECT m.*
FROM
media m
JOIN
media_likes ml
ON
ml.media_id = m.id
GROUP BY
ml.media_id
ORDER BY
COUNT(ml.id) DESC

Categories