Adding closure to select raw queries (sum) - laravel

I have many different combinations of select sum from the same table but with slightly different where queries.
foreach ($transactionTypes as $type) {
switch($type) {
case 'Fuel':
$values = \DB::table("transactions")
->select(\DB::raw("user_id, SUM(fuel_charge) as fuel_charges"))
->whereIn('type', ["Charge", "Refund"])
->whereIn('user_id', $userIdsToBeUsed)
->whereDate('created_at', '>=', $fromDate->toDateString())
->whereDate('created_at', '<=', $toDate->toDateString())
->groupBy('user_id')
->get();
break;
case 'Commission':
$userIdsToBeUsed = $userIds->merge($tier3Ids);
$values = \DB::table("transactions")
->select(\DB::raw("user_id, SUM(commission_charge) as commission_charges"))
->whereIn('user_id', $userIdsToBeUsed)
->whereDate('created_at', '>=', $fromDate->toDateString())
->whereDate('created_at', '<=', $toDate->toDateString())
->groupBy('user_id')
->get();
break;
}
}
As you can see, they are slightly different and I have a dozen of cases in the loop; but with this approach, I need to do a dozen queries to the same table.
What I want to ask is, Is there a way to combine them into a single query?
Something like:
$values = \DB::table("transactions")
->select(
[
\DB::raw("user_id, SUM(fuel_charge) as fuel_charges") => function($q) {
// $q->where(...)
}
],
[
\DB::raw("user_id, SUM(commission_charge) as commission_charges") => function($q) {
// $q->where(...);
}
])
->get()

A quick thought. This can be a pretty neat hack you can say:
$userIdsToBeUsed = $userIds->merge($tier3Ids);
foreach ($transactionTypes as $type) {
$values = \DB::table("transactions")
->select(\DB::raw("user_id, SUM(" . strtolower($type) . "_charge) as " . strtolower($type) . "_charges"))
->whereIn('type', ["Charge", "Refund"])
->whereIn('user_id', $userIdsToBeUsed)
->whereDate('created_at', '>=', $fromDate->toDateString())
->whereDate('created_at', '<=', $toDate->toDateString())
->groupBy('user_id')
->get();
}
I just use the lowercase of $type in query

Related

Laravel 8 - How to use where conditions for relation's column

hello friends can you help me to get the contents of $filter? I want to run where which is where the target column is in the relation array
$filter = $request->get('Biogear');
$data = DetailBarang::with(['barang' => function ($q) use ($filter) {
$q->where('brand', '=', $filter);
}])->get();
return response()->json($data);
you can try this
$filter = $request->get('Biogear');
$data = DetailBarang::with('barang')->whereHas('barang',function (Illuminate\Database\Eloquent\Builder $q) use ($filter) {
$q->where('brand', '=', $filter);
})->get();
return response()->json($data);

Laravel query not giving desired result

Can someone help fix this query. Query is returning null result for "j.code" and "accounts.title". And I am 100% sure that it should return result. I think my left join are missing something. Is this the proper way of using left join within a left join.
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal AS j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->where('j.vtype', '=', 'autostk.vtype')
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBY('date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
The raw query being generated is :
select `j`.`code`, `accounts`.`title`, `autostk`.* from `autostk`
left join (`journal` as `j` left join `accounts` on `j`.`code`=`accounts`.`code`)
on `autostk`.`refno` = `j`.`vno` and `j`.`code` >= ? and `j`.`branchid` = ?
and `j`.`vtype` = ? and `accounts`.`branchid` = ? where `autostk`.`branchid` = ?
and `autostk`.`itemcode` = ? and date(`autostk`.`date`) <= ? order by
`autostk`.`date` asc
UPDATE :
While checking the QueryLog i noticed that the binding for 'j'.'vtype' is "autostk.vtype"
Applying the query in workbench with 'autostk.vtype' returned null results.
But when I changed it to 'autostk'.'vtype' the results showed up correctly.
How to make this change in Laravel Eloquent ?
Please try like below:
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal as j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->where('j.vtype', '=', 'autostk.vtype')
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBy('autostk.date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
There is one problem:
->orderBy('autostk.date')
And you can use toArray() instead of map() like this
->orderBy('autostk.date')->get()->toArray();
Found the solution. Correct query is :
$query = DB::table('autostk')
->where('autostk.branchid', $branch_id)
->where('autostk.itemcode',$request->itemcode)
->whereDate('autostk.date','<=',$request->tdate)
->leftjoin('journal AS j', function($join) use ($branch_id) {
$join->on('autostk.refno', '=', 'j.vno')
->on('autostk.vtype', '=', 'j.vtype')
->where('j.code', '>=', 100)
->where('j.branchid', $branch_id)
->leftjoin('accounts', 'j.code', '=', 'accounts.code')
->where('accounts.branchid', $branch_id);
})
->select('j.code','accounts.title','autostk.*')
->orderBY('autostk.date')->get()
->map(function ($item, $key) {
return (array) $item;
})
->all();
->leftjoin('journal as j', function($join) use ($branch_id) {
as instead of AS

or where clause - Laravel eloquent

I have this query:
if($keyword){
array_push($where, ['name_en', 'LIKE', '%'.$keyword.'%']);
}
The problem is that I have and name_fr column and I need to use OR clause - array_push($where, ['name_fr', 'LIKE', '%'.$keyword.'%']).
I can't use ->orWhere, because I have many dynamic search fields - they may exists or not.
For the example:
if($fromPrice){
array_push($where, ['price', '>=', $fromPrice]);
}
if($toPrice){
array_push($where, ['price', '<=', $toPrice]);
}
And the query is:
$cars= Property::with(array(
'photos'=>function($query){
$query->select(['car_id', 'name']);
}
))->where($where)->paginate(10);
I need to select WHERE name_en LIKE %val% OR name_fr LIKE %val% with another queries.
Is there a way to use somehow where, 'OR' and LIKE in the above way including another values from $where array ?
to achieve that a suggest you to divide your query as below and don't put your keyword condition within $where array:
$query = Property::with(array(
'photos'=>function($query){
$query->select(['car_id', 'name']);
}
))->where($where);
if($keyword){
$query = $query->where(function ($query) use ($keyword) {
$query->where('name_fr', 'like', $keyword)
->orWhere('name_en', 'like', $keyword);
});
}
$cars = $query->paginate(10);
You can also go with
$propertyQuery = Property::query();
if($keyword){
$propertyQuery->where(function($query) use ($keyword){
$query->where('name_fr', 'LIKE', '%'.$keyword.'%')->orWhere('name_en', 'LIKE', '%'.$keyword.'%');
});
}
$cars = $propertyQuery->paginate(10);

How can I Refactor this search filter query?

I need to search in multiple tables. I'm checking every single request object. For example: I am checking if the request has that object then concating it to my main query and getting that query result at the last. It doesn't looks and isn't good. How can I make this search query filter better in laravel?
Note: I have searched questions in stackoverflow but they are dealing with only one model.
$query = DB::table('clients')
->leftjoin('ecommerce_contacts','ecommerce_contacts.client_id', '=', 'clients.id')
->select('ecommerce_contacts.*', 'clients.*')
->where('clients.is_deleted', '=', '0');
if(!is_null($request->fname)){
$query+=->where('clients.fname', 'like', '%$request->fname%');
}
if(!is_null($request->lname)){
$query+=->where('clients.lname', 'like', '%$request->lname%');
}
if(!is_null($request->gender)){
$query+=->where('clients.sex', $request->sex);
}
if(!is_null($request->number)){
$query+=->where('ecommerce_contacts.sex', 'like', $request->number);
}
if(!is_null($request->registered_date)){
}
if(!is_null($request->purchase)){
}
$client = $query->get();
$data = json_encode($clients);
return $data;
Use conditional clauses:
DB::table('clients')
->leftjoin('ecommerce_contacts','ecommerce_contacts.client_id', '=', 'clients.id')
->select('ecommerce_contacts.*', 'clients.*')
->where('clients.is_deleted', '=', '0')
->when(request()->has('fname'), function ($query) {
return $query->where('clients.fname', 'like', '%' . request()->fname . '%');
})
->when(request()->has('lname'), function ($query) {
return $query->where('clients.lname', 'like', '%' . request()->lname . '%');
})
->when(request()->has('gender'), function ($query) {
return $query->where('clients.sex', '=', request()->gender);
})
...
$query=DB::table('clients')
->leftjoin('ecommerce_contacts','ecommerce_contacts.client_id', '=', 'clients.id')
->select('ecommerce_contacts.*', 'clients.*')
->where('clients.is_deleted', '=', '0');
$search_fields=['fname','lname','gender','number','registered_date','purchase'];
foreach($search_fields as $key){
if(!is_null($key)){
$query->orWhere('clients.'.$key, 'LIKE', '"%" . '.$request->$key.' . "%"');
}
}
$client = $query->get();
$data = json_encode($client);
return $data;
I upvoted #DigitalDrifter 's answer because i liked it but I prefer my filter pattern.
Have a look at this:
$query = DB::table('clients')
->leftjoin('ecommerce_contacts','ecommerce_contacts.client_id', '=', 'clients.id')
->select('ecommerce_contacts.*', 'clients.*')
->where('clients.is_deleted', '=', '0');
!isset($request->fname) ?: $query->where('clients.fname', 'like', '%$request->fname%');
!isset($request->lname) ?: $query->where('clients.lname', 'like', '%$request->lname%');
!isset($request->gender) ?: $query->where('clients.sex', $request->sex);
!isset($request->number) ?: $query->where('ecommerce_contacts.sex', 'like', $request->number);
$client = $query->get();
$data = json_encode($clients);
return $data;
I think this is more readable and requires less line of code.

How can I make this search query more efficient?

I have a somewhat convoluted search query that I would like to make more efficient (if possible).
Here is the entire code for this query:
Route::post('api/search/{startRow}', function($startRow)
{
$category = Category::where('name', '=', Input::get('category'))->first();
// Initialize query
$query = Resource::with('alerts', 'alerts.type', 'user', 'category', 'comments', 'comments.comments', 'ratings')
->where('duplicate', '=', 0);
// Limit search results
if(Input::get('show'))
{
$show = Input::get('show');
switch ($show) {
case 'verified':
$query->where('verified', '=', true);
break;
case 'unverified':
$query->where('verified', '=', false);
break;
case 'alerted':
$query->has('alerts');
break;
case 'unalerted':
$query->has('alerts', '=', 0);
break;
default:
// The default will be 'all' (show all results)
break;
}
}
if($category->name != "everything")
$query->where('category_id', '=', $category->id);
// Sort the search results
if(Input::get('sort_type'))
{
$sort_by = Input::get('sort_type');
switch ($sort_by)
{
case 'relevance':
break;
case 'name_asc':
$query->orderBy('name', 'asc');
break;
case 'name_desc':
$query->orderBy('name', 'desc');
break;
case 'rating_high':
$query
->leftJoin('ratings', 'ratings.ratable_id', '=', 'resources.id')
->where('ratings.ratable_type', '=', 'Resource')
->orderBy(DB::raw('avg(ratings.score)'), 'desc')
->orderBy(DB::raw('count(ratings.score)'), 'desc')
->select('resources.*')
->groupBy('resources.id');
break;
case 'rating_low':
$query
->leftJoin('ratings', 'ratings.ratable_id', '=', 'resources.id')
->where('ratings.ratable_type', '=', 'Resource')
->orderBy(DB::raw('avg(ratings.score)'), 'asc')
->orderBy(DB::raw('count(ratings.score)'), 'asc')
->select('resources.*')
->groupBy('resources.id');
break;
case 'date_new':
$query->orderBy('created_at', 'desc');
break;
case 'date_old':
$query->orderBy('created_at', 'asc');
break;
default:
break;
}
}
// Search by keyword(s)
if(Input::get('keyword'))
{
$search = Input::get('keyword');
$searchTerms = explode(' ', $search);
$fields = array(
'resources.description',
'resources.website',
'resources.additional_info');
foreach ($searchTerms as $term)
{
$query->where('resources.name', 'LIKE', '%'. $term .'%');
foreach ($fields as $field)
{
$query->orWhere($field, 'LIKE', '%'. $term .'%');
}
}
}
// Search by tag(s)
if(Input::get('tags'))
{
$tags = Input::get('tags');
$query
->select('resources.*')
->join('taggables', 'taggables.taggable_id', '=', 'resources.id')
->join('tags', 'taggables.tag_id', '=', 'tags.id')
->whereIn('tags.id', $tags)
->groupBy('resources.id')
->havingRaw('COUNT(resources.id)=?', array(count($tags)));
}
// Total number of results
$count = $query->get()->count();
// Page number and offset for infinite scroll
$query->skip($startRow)->take(10);
// Get our first set of tiles
$tiles = $query->get();
return Response::json(array(
'count' => $count,
'tiles' => $tiles->toArray()));
});
You see, I have a database filled with "resources" which (through pivot tables) are related to tags, comments and alerts, and I want these resources searchable on any of the following criteria:
Text contained in resource model itself, tags associated with the resource, and number of associated alerts.
One problem I'm having is that the keyword search doesn't seem to be "accurate" enough. When I search for, say, "Venture Firm", there are a few results returned before the one which contains the phrase "Venture Firm" - a user will definitely not expect this.
Another problem I'm having related to selecting a "show" type (i.e. $query->has('alerts') if user only wants to see resources with alerts). If I enter a keyword search and a show type (like above), the results will still contain resources that do not have alerts (even though I specified I only want resources that have alerts).
Relevance search depends on your db engine.
But for the keyword search, you have it wrong:
foreach ($fields as $field)
{
$query->orWhere($field, 'LIKE', '%'. $term .'%');
}
This piece adds WHERE ....long list of clauses here.... OR something LIKE %term% ... what basically breaks the whole thing.
Instead you need this:
$fields = array(
'resources.name',
'resources.description',
'resources.website',
'resources.additional_info'
);
$query->where(function ($q) use ($searchTerms, $fields) {
foreach ($searchTerms as $term)
{
foreach ($fields as $field)
{
$q->orWhere($field, 'LIKE', '%'. $term .'%');
}
}
});
This will wrap your OR .. OR .. clauses in AND ( .. OR .. ).

Resources