Laravel order by not working on related columns on datatable - laravel

Basically, my backend is:
$columns = array(
0 => 'purchased_date',
1 => 'supplier',
2 => 'purchased_code',
3 => 'grand_total',
4 => 'paid_amount',
5 => 'overall_due_amount',
6 => 'payment_status',
7 => 'action',
);
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$dir = $request->input('order.0.dir');
$query = Purchase::with(['supplier:id,name','purchasePayments']);
$purchased_date = $request->columns[0]['search']['value'];
if (!empty($purchased_date)) {
$query->where('purchased_date', 'like', '%' . $purchased_date . '%');
}
$supplier_search = $request->columns[1]['search']['value'];
if (!empty($supplier_search)) {
$query->whereHas('supplier', function($q) use($supplier_search){
$q->where('name', 'like', '%' . $supplier_search . '%');
});
}
$purchased_code = $request->columns[2]['search']['value'];
if (!empty($purchased_code)) {
$query->where('purchased_code', 'like', '%' . $purchased_code . '%');
}
$totalData = $query->count();
//HERE HOW COULD I ORDER USING SUPPLIER NAME
//suppliers is the name of the table
//and supplier is the relation defined inside purchase model
$order = 'suppliers.name';
$query->orderBy($order, $dir);
if ($limit != '-1') {
$query->offset($start)->limit($limit);
}
$records = $query->get();
$totalFiltered = $totalData;
$data = array();
if (isset($records)) {
foreach ($records as $k => $v) {
$nestedData['id'] = $v->id;
$nestedData['purchased_date'] = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $v->purchased_date)->format('Y-m-d');
$nestedData['supplier'] = $v->supplier->name;
$nestedData['purchased_code'] = $v->purchased_code;
$nestedData['grand_total'] = $v->grand_total;
$nestedData['paid_amount'] = $v->purchasePayments->sum('paid_amount');
$nestedData['overall_due_amount'] = $v->overall_due_amount;
$nestedData['payment_status'] = PaymentHelper::getPaymentStatusBadge($v->purchasePayments->last()->payment_status);
$nestedData['action'] = \View::make('admin.purchases.action')->with('r', $v)->render();
$data[] = $nestedData;
}
}
Here, how could I order the full results based on the supplier name. Here, suppliers is the name of the table and supplier is the name of the relation defined on the purchase model. Is there any way to deal with this situation where I need to order based on the related model

It's because eager loading isn't building your query as you anticipate.
// Instead of:
$query->orderBy($order, $dir); ❌
// Use this:
$query->join('suppliers', 'purchases.id', '=', 'suppliers.purchase_id')
->orderBy('suppliers.name', $dir) ✅
To avoid ambiguous column names. i.e: id (Between purchases and suppliers tables.), modify:
// Instead of:
$records = $query->get(); ❌
// Use this:
$records = $query->get(["purchases.*"]); ✅
NOTE: Eager loading still works with the above implementation.

Related

Laravel query groubBy condition

I have a dB query where I would like to groupBy() only when conditions are met without using union because of pagination.
Unfortunately groupBy() seems to only work when called on the entire query outside of the loop.
This was made for dynamic filtering from $filterArr. Depending on the array I need to select from different columns of the table.
When the $key=='pattern' I would need the distinct results from its column.
the query looks something like this
select `col_1`, `col_2`, `col_3`
from `mytable`
where (`color` LIKE ? or `pattern` LIKE ? or `style` LIKE ?)
group by `col_2` //<< i need this only for 'pattern' above and not the entire query
Heres the model:
// $filterArr example
// Array ( [color] => grey [pattern] => stripe )
$query = DB::table('mytable');
$query = $query->select(array('col_1', 'col_2', 'col_3'), DB::raw('count(*) as total'));
$query = $query->where(function($query) use ($filterArr){
$ii = 0;
foreach ($filterArr as $key => $value) {
if ($key=='color'){
$column = 'color';
}else if ($key=='style'){
$column = 'style';
}else if ($key=='pattern'){
$column = 'pattern';
$query = $query->groupBy('col_2'); // << !! does not work
}
if($ii==0){
$query = $query->where($column, 'LIKE', '%'.$value.'%');
}
else{
$query = $query->orWhere($column, 'LIKE', '%'.$value.'%');
}
$ii++;
}
});
$query = $query->orderBy('col_2', 'asc')->simplePaginate(30);
I think you can simplify your code a bit:
$query = DB::table('mytable');
$query = $query->select(array('col_1', 'col_2', 'col_3'), DB::raw('count(*) as total'));
$query = $query->where(
collect($filterArr)
->only(['color','style','pattern'])
->map(function ($value, $key) {
return [ $key, 'like', '%'.$value.'%', 'OR' ];
})->all()
)->when(array_key_exists('pattern', $filterArr), function ($query) {
return $query->groupBy('col_2');
});
$query = $query->orderBy('col_2', 'asc')->simplePaginate(30);

Laravel query builder. How to make the query shorter?

I need to prepare data for datatables, and i need to count total amount of filtered data of the query, but i cant do it with just one query because of limit so i must do almost the same query but with no limit option for pagination, and it makes the amount of my code x2. Is it possible to make this code shorter and beautiful?
public function data($request)
{
$staff = auth()->user();
$role = Role::find($staff->role_id)->alias;
$isAdmin = $role == 'admin';
$columns = ['id', 'name', 'shop', 'hash', 'status'];
$limit = $request->input('length');
$start = $request->input('start');
$order = $columns[$request->input('order.0.column')];
$directions = $request->input('order.0.dir');
$searchValue = $request->input('search.value');
$filters = explode(',', $request->input('columns.7.search')['value']);
foreach ($filters as $key => $value) {
if ($value !== "") {
array_push($shops, $value);
}
}
if ($searchValue) $filters[] = $searchValue;
$nfcs = NfcMark::offset($start)->select('nfc_marks.*')
->leftJoin('shops as s', 's.id', 'nfc_marks.shop_id')
->when(!$isAdmin, function ($q) {
$shop_ids = $this->shopRepository->shopsIdsByOwner(auth()->user()->id);
return $q->whereIn('shop_id', $shop_ids);
})
->when($searchValue, function ($q) use ($filters) {
foreach ($filters as $filter) {
$q->orWhere('shops.name', 'LIKE', '%' . $filter . '%');
}
})
->limit($limit)
->orderBy($order, $directions)
->get();
$nfcs_count = NfcMark::query()
->leftJoin('shops as s', 's.id', 'nfc_marks.shop_id')
->when(!$isAdmin, function ($q) {
$shop_ids = $this->shopRepository->shopsIdsByOwner(auth()->user()->id);
return $q->whereIn('shop_id', $shop_ids);
})
->when($searchValue, function ($q) use ($filters) {
foreach ($filters as $filter) {
$q->orWhere('shops.name', 'LIKE', '%' . $filter . '%');
}
})->get();
$totalFiltered = count($nfcs_count);
$totalData = $totalFiltered;
$data = array();
if (!empty($nfcs)) {
foreach ($nfcs as $item) {
$nestedData['id'] = $item->id;
$nestedData['name'] = $item->name;
$nestedData['shop'] = $item->shop_id;
$nestedData['hash'] = $item->hash;
$nestedData['status'] = $item->status;
$data[] = $nestedData;
}
}
$json_data = array(
"draw" => intval($request->input('draw')),
"recordsTotal" => intval($totalData),
"recordsFiltered" => intval($totalFiltered),
"data" => $data
);
return json_encode($json_data);
}
You can hold the part of the query that is the same between those as a builder and run both queries from it if you feel you need both these queries:
$query = NfcMark::leftJoin('shops as s', 's.id', 'nfc_marks.shop_id')
->when(!$isAdmin, function ($q) {
$q->whereIn(
'shop_id',
$this->shopRepository->shopsIdsByOwner(auth()->user()->id)
);
})
->when($searchValue, function ($q) use ($filters) {
foreach ($filters as $filter) {
$q->orWhere('shops.name', 'LIKE', '%' . $filter . '%');
}
});
$nfcs_count = $query->get();
$nfcs = $query->select('nfc_marks.*')
->offset($start)
->limit($limit)
->orderBy($order, $directions)
->get();
You can also get the count without returning the rows and just using count() instead of get()
You can use clone to duplicate the query and then run it with different where statements.
$query1 = NfcMark::query()
->leftJoin('shops as s', 's.id', 'nfc_marks.shop_id')
->when(!$isAdmin, function ($q) {
$shop_ids = $this->shopRepository->shopsIdsByOwner(auth()->user()->id);
return $q->whereIn('shop_id', $shop_ids);
})
->when($searchValue, function ($q) use ($filters) {
foreach ($filters as $filter) {
$q->orWhere('shops.name', 'LIKE', '%' . $filter . '%');
}
})
$query2 = clone $query1;
$nfcs_count = $query1->get() //or $query1->count();
$$nfcs = $query2
->limit($limit)
->orderBy($order, $directions)
->get();

Laravel : Get Related Child Records From A variable containing Found Records

I am trying to set up some filter options: one is for contact name which will operate on contact table / and other filter for date range which will operate on Sale table (created_at column).
After I store request inputs into variables I do this:
$contacts = Contact::where('first_name', 'like', '%'.$name.'%')
->orWhere('last_name', 'like', '%'.$name.'%')->get();
Then I search for Sale records inside date range like this:
$sales = Sale::whereBetween('created_at', [$fromDate, $toDate])->get();
Then I have this:
$data = [];
foreach ($contacts as $contact) {
$data[] = [
'contact' => $contact->first_name . " " . $contact->last_name,
'sale' => $contact->sales->sum('amount_spend'),
'people'=>$contact->sales->sum('people_with')
];
}
dd($data);
What I want is for each contact on $contacts to find the sum of related sale::amount_spend, but I want it to look for related sale records inside $sales variable (found set) and not all the table.
Any help appriciated,
$data = Contact::with([
'sales'=> function ($query) use($fromDate, $toDate){
$query->whereBetween('created_at', [$fromDate, $toDate]);
}])
->where('first_name', 'like', '%'.$name.'%')
->orWhere('last_name', 'like', '%'.$name.'%')
->get()
->map(function($item){
return [
'contact' => $item->first_name . " " . $item->last_name,
'sale' => $item->sales->sum('amount_spend'),
'people'=>$item->sales->sum('people_with')
];
});

Laravel combine query with if statement

Im working on product filtering using AJAX. Is there any possible way to produce same output as picture shown below using query builder?
I have tried union but it’s not working.
I hope this example gives idea , Try this one
use multiple if statement and get data into DB using joins .
function datatables($request) {
$data = $this->leftJoin('blog_category', 'blog_category.blog_category_uuid', '=', 'blog_detail.blog_category_uuid')
->where('blog_detail.blog_detail_is_deleted', 'NO');
if ($request->search['value'] != null && $request->search['value'] != '') {
$keyword = $request->search['value'];
$data = $data->where(function($query) use ($keyword) {
// $query->orWhere('activity_type.activity_type_name', 'LIKE', '%' . $keyword . '%');
$query->orWhere('blog_detail.blog_detail_title', 'LIKE', '%' . $keyword . '%');
});
}
if (isset($request->order[0]['dir'])) {
$data = $data->orderBy('blog_detail.blog_detail_id', $request->order[0]['dir']);
} else {
$data = $data->orderBy('blog_detail.blog_detail_created_date');
}
$datacount = $data->count();
$dataArray = $data->select('blog_detail.*', 'blog_category.blog_category_name' , DB::raw('DATE_FORMAT(blog_detail.blog_detail_created_date,"%Y-%m-%d") as blog_detail_date'));
if ($request->length == -1) {
$dataArray = $dataArray->get();
} else {
$dataArray = $dataArray->skip($request->start)->take($request->length)->get();
}
return [$datacount, $dataArray];
}
In laravel you can create a model for product say Product. Then the query will be like
$products = Product::where('product_status', '1');
if ($request->input('minimum_price') && $request->input('maximum_prize')) {
$products = $products->whereBetween('product_prize', array($request->input('minimum_price'), $request->input('maximum_prize')));
}
if ($request->input('brand')){
$brand_filter = implode("','", $request->input('brand'));
$products = $products->whereIn('product_brand', $brand_filter);
}
$products = $products->get();
after the execution $products contains the products after query.

How to use Laravel Eloquent with LIKE statement in array list?

I have some conditions in array like
$category = Input::get('category');
$cuisine = Input::get('cuisine');
$veg = Input::get('veg');
$trending = Input::get('trending');
$time = Input::get('time');
if($category) $conditions['category'] = $category;
if($cuisine) $conditions['cuisine'] = $cuisine;
if($veg) $conditions['veg'] = $veg;
if($trending) $conditions['trending'] = $trending;
How can I make
$list = Data::where($conditions)->where('cuisine','LIKE','%'.$cuisine.'%')->get();
Is it possible to enter LIKE % in this statement
if($cuisine) $conditions['cuisine'] = $cuisine;
The problem is that if I want to add this where('cuisine','LIKE','%'.$cuisine.'%') several areas it needs to be updated. and in some cases, if cuisine is not present everything cannot be fetched
I want to perform LIKE statement for only cuisine data.
Sure, you can do that by creating an array with this format:
[['column1', 'like', '%' . $filter1 . '%'], ['column2', 'like', '%' . $filter2 . '%']]
For example:
$fields = ['category', 'cuisine', 'veg', 'trending', 'time'];
foreach ($fields as $field) {
if ($request->get($field)) {
$conditions[] = [$field, 'like', '%' . $request->get($field) . '%'];
}
}
$list = Data::where($conditions)->get();
Another example from the docs:
You may also pass an array of conditions to the where function:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
https://laravel.com/docs/5.5/queries#where-clauses
Update
You've just updated your question and said you want to use like only for $cuisine. In this case, you can use a closure:
->where(function($q) use($request) {
if ($request->cuisine) {
$q->where('cuisine', 'like', '%' . $request->cuisine . '%');
}
})
Or you could use when():
->when($request->cuisine, function ($q) use ($cuisine) {
return $q->where('cuisine', 'like', '%' . $request->cuisine . '%');
})
Well, you can do it in parts:
$query = Data::where($conditions);
if($cuisine) {
$query->where('cuisine','LIKE','%'.$cuisine.'%');
}
$list = $query->get();
You can do it like,
$query = DB::table('data');
$category = Input::get('category');
$cuisine = Input::get('cuisine');
$veg = Input::get('veg');
$trending = Input::get('trending');
$time = Input::get('time');
if($category) {
$query->where('category','LIKE','%'.$category.'%');
}
if($cuisine) {
$query->where('cuisine','LIKE','%'.$cuisine.'%');
}
if($veg) {
$query->where('veg','LIKE','%'.$veg.'%');
}
if($trending) {
$query->where('trending','LIKE','%'.$trending.'%');
}
if($time) {
$query->where('time','LIKE','%'.$time.'%');
}
$list = $query->get();
I hope you will understand.
Why not just assign as blank value as default as it will pass in LIKE for all cases
$conditions['cuisine']= (isset($cuisine)&&$cuisine)) ? $cuisine : '';
Well, you have to assign query to some variable:
$query = Data::where($conditions);
if($cuisine) {
$query = $query->where('cuisine','LIKE','%'.$cuisine.'%');
}
$list = $query->get();

Resources