Laravel foreach "where" with eloquent - laravel

I have certain fields and data values which cannot be hardcoded into the query. I'm trying to get something like this:
return Listing::where('id', $id)
->where(function($query) use ($input) {
->where('field_1', 'foo_1')
->where('field_2', 'foo_2')
->where('field_3', 'foo_3')
}
->get();
**Here's what I have **
return Listing::where('id', $id)
->where(function($query) use ($input) {
$i = 0;
foreach ($input as $key => $value) {
$i++;
// ->where('field_1', red_1); // Desired output
->where("where(field_{$i},".$value."_1)");
// $query = $query."where(field_{$i},".$value."_1)"."<br>";
// return $query prints out the following
/*
field_1 red_1,
field_2 foo_1,
field_3 bar_3
*/
}
})
->get();

Something like this should work:
$listing = Listing::where('id', $id);
foreach ($input as $key => $value) {
$i++;
// ->where('field_1', red_1); // Desired output
$listing->where("where(field_{$i},".$value."_1)");
}
$results = $listing->get();

$query = Listing::where('id', $id);
$i = 0;
foreach ($input as $key => $value) {
$i++;
$query->where('field_'.$i,$value.'_'.$i);
}
return $query->get();
One you're not chaining correctly and two you are mis-using the querybuilder closure. If you want to execute logic like a loop then you have to break down the query. Furthermore using a where closure is like writing a parenthesis around your where conditions.
Something like:
$query->where('bacon', $foo)
$query->where(function ($query) use ($bar, $baz){
$query->where('apple', $bar);
$query->orWhere('orange', $baz)
});
Would roughly translate to:
WHERE bacon = $foo AND (apple = $bar OR orange = $baz)

Related

Refactor Query Builder Laravel

I want to do repetitive get data and foreach on several tables (see example below). Is there a way to write the code in a cleaner way instead of repeating the same code for all the tables?
$xs = DB::table('table1')->where('text', 'like', '%string')->get();
foreach ($xs as $x) {
..
}
$ys = DB::table('table2')->where('text', 'like', '%string')->get();
foreach ($ys as $y) {
..
}```
My approach is using array and foreach
$tables = ['table1', 'table2'];
results = [];
foreach($tables as $table) {
$data = DB::table($table)->where('text', 'like', '%string')->get();
foreach($data as $d) {
// your logic here
}
$results[] = ; // return a value from each query to array
}
You can write a base function and pass tableName to it and execute certain action
public function getData($tableName) {
$query = DB::table($tableName)->where('text', 'like', '%string')->get();
foreach ($query as $row) {
...
}
// return result;
}
$tables = ['table1', 'table2', 'table3'];
$queryResponse = [];
foreach($tables as $tableName) {
$queryResponse[$tableName] = $this->getData($tableName);
}

How to use regex or any other operator with whereIn clause

Is there any way we can use operator or regex as we normally do with the whereIn() clause.
I want to use something like this
$query->whereIN(name,'like','%test%');
I'm getting multiple data in an array. It should return data if name contains any of the keywords.
For eg. $searchArray = ['test','case'];
So it should return data with name containing values in $searchArray
Is it possible?
The whereIn method verifies that a given column's value is contained within the given array:
$users = DB::table('users')
->whereIn('id', [1, 2, 3])
->get();
https://laravel.com/docs/9.x/queries#additional-where-clauses
Try this:
where('title', 'like', '%' . $keyword . '%')
UPDATE:
$query->where(function ($query) use ($keyword, $columns) {
foreach ($columns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$query->$clause($column, "LIKE", "%$keyword%");
if (!empty($relativeTables)) {
$this->filterByRelationship($query, $keyword,
$relativeTables);
}
}
});
I Had the same problem. The only way i was able do solve, was with mongodb native query aggregation. Let me show how i did.
I made a function to use in all of my system
public function matchLikeIn($match, $field, $value,$option = 'i')
{
try {
$or = array();
foreach($value as $v){
$or['$or'][] = [
$field => [
'$regex' => $v,
'$options' => $option
]
];
}
$match['$and'][] = $or;
return $match;
} catch (Exception $e) {
}
}
A exemple using it
$match = array();
$names = ["name1","name2"];
$match = $this->matchLikeIn($match, "name", $names, 'i');
$aggregate[] = ['$match' => $match];
$query = Model::raw(function ($collection) use ($aggregate) {
return $collection->aggregate($aggregate);
})->toArray();

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 - passing array of columns in 'where' clause

I have a search query like this:
$data = User::where('first_name', 'like', '%'.$query.'%')
->orWhere('last_name', 'like', '%'.$query.'%')
->get();
Now, I have many models, each with different column names. Instead of defining a search() function into every controller, I want to do this:
// User
public static function searchableFields()
{
return ['first_name', 'last_name'];
}
// Some other model
public static function searchableFields()
{
return ['name', 'description'];
}
And put the search logic in a shared controller, something like this:
$data = $classname::
where($classname::searchableFields(), 'like', '%'.$query.'%')
->get();
How can I achieve this?
Thanks a lot.
You can loop over the fields and add them to your Eloquent query one by one.
$data = $classname::where(function ($query) use ($classname) {
foreach ($classname::searchableFields() as $field)
$query->orWhere($field, 'like', '%' . $query . '%');
})->get();
I would use scope for that.
You can create base model that all the models should extend (and this model should extend Eloquent model) and in this model you should add such method:
public function scopeMatchingSearch($query, $string)
{
$query->where(function($q) use ($string) {
foreach (static::searchableFields() as $field) {
$q->orWhere($field, 'LIKE', '%'.$string.'%');
}
});
}
Now you can make a search like this:
$data = User::matchingSearch($query)->get();
Just to avoid confusion - $query parameter passed to matchingSearch becomes $string parameter in this method.
You can try something like this.
// Controller
function getIndex(Request $request)
{
$this->data['users'] = User::orderBy('first_name','asc')->get();
if ($request->has('keyword')) {
$results = User::search($request->keyword);
$this->data['users'] = collect([$results])->collapse()->sortBy('first_name');
}
}
// Model
function search($keyword)
{
$results = [];
$search_list = ['first_name','last_name'];
foreach ($search_list as $value)
{
$search_data = User::where($value,'LIKE','%'.$keyword.'%')->get();
foreach ($search_data as $search_value) {
$exist = 0;
if (count($results)) {
foreach ($results as $v) {
if ($search_value->id == $v->id) {
$exist++;
}
}
if ($exist == 0) {
$results[] = $search_value;
}
} else{
$results[] = $search_value;
}
}
}
return $results;
}

Resources