Laravel filters system with multi checkbox - php

I am using laravel 5.4 and I want to filter search. its my front-end
my code looks like this
class DeviceFilterController extends Controller
{
public function filter(Feature $feature){
$marks = isset($_POST["mark"]) ? $_POST["mark"] : null;
$feature = $feature->newQuery();
if(isset($marks ))
{
foreach ($marks as $value)
{
$feature->where('device_mark', $value);
}
}
return $feature->get();
}
}
that result just one entry

You could take a different approach using whereIn(), assuming you mark input is an array of IDs like [1, 4, 8, 22].
public function filter(Request $request)
{
$features = Feature::when($request->has('mark'), function($query) use ($request) {
return $query->whereIn('device_mark', $request->mark); //Assuming you are passing an array of IDs
})->get();
return $features;
}
when() closure will only executes when you are sending 'mark' input. Doing it without when would look like this
public function filter(Request $request)
{
$features = [];
if ( $request->has('mark') && $request->mark != '' ) {
$features = Feature::whereIn('device_mark', $request->mark)->get();
} else {
$features = Feature::get();
}
return $features;
}

Related

Laravel Query Builder returning all rows despite 'where' statement

I'm making a search service using the following blog post:https://m.dotdev.co/writing-advanced-eloquent-search-query-filters-de8b6c2598db
This seems to work except for the fact that my queries are returning every row in the table rather than specific ones.
For example, I have a filter such as this:
public static function apply(Builder $builder, $value)
{
return $builder->whereHas('items', function ($q) use ($value) {
$q->where('item_id', $value);
});
}
This works on the items relationship on my model. Running this query conventionally seems to work, but it fails in relation to the code in the above blog post.
The same is true for an even simpler query:
public static function apply(Builder $builder, $value)
{
return $builder->where('name', $value);
}
When I run my test it just gives me every single item in the table rather than those matching my condition.
My search code looks like this and I can't see any obvious errors:
public static function search(Request $filters)
{
$query =
static::applyDecoratorsFromRequest(
$filters,
(new User)->newQuery()
);
return static::getResults($query);
}
private static function applyDecoratorsFromRequest(Request $request, Builder $query)
{
foreach ($request->all() as $filterName => $value) {
$decorator = static::createFilterDecorator($filterName);
if (static::isValidDecorator($decorator)) {
$query = $decorator::apply($query, $value);
}
}
return $query;
}
Any help appreciated!
it's because you should use local scopes instead : https://laravel.com/docs/5.8/eloquent#local-scopes
public static function scopeWhereItem(Builder $query, $value)
{
return $query->whereHas('items', function (query) use ($value) {
$query->where('item_id', $value);
});
}
Then in your controller :
// Initiate the queryBuilder
$query = YourModel::query();
// your request is like : ['item' => '2'];
foreach($request->all()sas $filterName => $value) {
// Build the scope Name (whereItem())
$scope_name = 'where' . ucFirst($filterName);
$query->$scopeName($value);
}
$results = $query->get();
you can add some checks if the method 'scope' . ucFirst($scopeName) exists if you want
it will generate a query like this : YourModel::query()->whereItem(2)->get()'

What will the best solution for this multiple optional filter?

These are all optional fields, so will I have to write multiple queries with conditions or is there any way to handle this using Laravel? What will be the query looks like?
Thanks
It depends a bit on how the filters are submitted, but you can do one of the following two things (and probably a gazillion more...):
public function listCars(Request $request)
{
$cars = Car::when($request->get('make'), function ($query, $make) {
$query->where('make', $make);
})
->when($request->get('model'), function ($query, $model) {
$query->where('model', $model);
})
->...
->get();
// do what you have to do
}
So you are basically wrapping your query builder calls in when($value, $callback), which will only execute $callback if $value evaluates to true. When you retrieve a not set parameter with $request->get('parameter'), it will return null and the callback is not executed. But be careful, if $value is 0 it will also not execute the callback. So be sure you don't have this as an index.
As alternative to this, you can also do the same thing but with a bit less eloquent expressions...
public function listCars(Request $request)
{
$query = Car::query();
if($request->filled('make')) {
$query->where('make', $request->get('make'));
}
if($request->filled('model')) {
$query->where('model', $request->get('model'));
}
// some more filtering, sorting, ... here
$cars = $query->get();
// do what you have to do
}
Here is a working example of something similar query i have in my app.
$filters = $vehicle->newQuery();
if (!empty($request->make)) {
$filters->where('make_id', $request->make);
}
if (!empty($request->carmodel)) {
$filters->where('carmodel_di', $request->carmodel);
}
if (!empty($request->year)) {
$filters->where('year_id', $request->year);
}
if (!empty($request->engine)) {
$filters->where('engine_id', $request->engine);
}
if (!empty($request->price)) {
$filters->where('price_id', $request->price);
}
$cars = $filters->latest()->paginate(50);
and now push the $cars variable to view. I hope this works for you or atleast gives you an idea on how to proceed
here is a simple way, you can also make the joins conditional inside the ->when() condition, if you are in Laravel version > 5.4, use $request>filled() instead of $request->has()
public function listCars(Request $request)
{
$cars = Car::when($request->has('make'), function ($query)use($request) {
$query->join('maker','car.makerId','=','maker.id')
->where('make', $request->input('make'));
})
->when($request->has('model'), function ($query)use($request) {
$query->where('model', $request->input('model'));
})
->...
->get();
// you can even make the join conditionaly,
}
$fiterItem = ['make','model','year','engine','price'];
$filters = $vehicle->newQuery();
foreach ($filter as $item) {
if ($r->filled($item)) {
$list->where($item, $r->query($item));
}
}
$list = $filters->paginate(20);

Merge laravel 4 multiple relashionships

I have two relashionships in laravel v4.2. When I merge these two relashion into third function and then I call third function then I receive Uncought exception. Below is my code
public function following_friend() {
return $this->hasOne('Friend', 'following_id', 'id');
}
public function follower_friend() {
return $this->hasOne('Friend', 'follower_id', 'id');
}
public function mutual_friends() {
return $this->following_friend->merge($this->follower_friend);
}
public static function get_users_infomation_by_ids($login_id, $users_arr = array()) {
$users = User::where(function($sql) use($login_id, $users_arr) {
$sql->whereIn('id', $users_arr);
})
->with('mutual_friends')
->get(array('id', 'username', 'full_name', 'is_live', 'message_privacy', 'picture'));
return (!empty($users) && count($users) > 0) ? $users->toArray() : array();
}
I don't know that where is the problem in merging these two relashionships.
Try to write like below :-
public static function get_users_infomation_by_ids($login_id, $users_arr = array()) {
$users = User::with('mutual_friends')
->where(function($sql) use($login_id, $users_arr) {
$sql->whereIn('id', $users_arr);
})
->get(array('id', 'username', 'full_name', 'is_live', 'message_privacy', 'picture'));
return (!empty($users) && count($users) > 0) ? $users->toArray() : array();
}
Since I have used like this for one of my Project :-
public function getShipmentDetails($shipmentId = null) {
$response = Shipment::with('pdDetails','shipmentDocuments')
->where('id', $shipmentId)
->first();
if($response) {
return $response->toArray();
}
}
It works for me.

Complex query and getting results by slug instead of ID in laravel

I have complex query and relation which I'm not fully understand. I'm kind of new in Laravel. Anyway, I'm looking for a way to load this with slugs instead of ID's.
This is the function in the controller
public function index( $category_id)
{
$Category = new Category;
$allCategories = $Category->getCategories();
$category = Category::find($category_id);
if($category->parent_id == 0) {
$ids = Category::select('id')->where('parent_id', $category_id)->where('parent_id','!=',0)->get();
$array = array();
foreach ($ids as $id) {
$array[] = (int) $id->id;
}
$items = Item::whereIn('category_id',$array)->where('published', 1)->paginate(5);
} else {
$items = Item::where('category_id' ,$category_id)->where('published', 1)->paginate(5);
}
return view('list', compact('allCategories','items'));
}
Those are relations in the Model
public function item()
{
return $this->hasMany('App\Item','category_id');
}
public function children()
{
return $this->hasMany('App\Category', 'parent_id');
}
public function getCategories()
{
$categoires = Category::where('parent_id',0)->get();
$categoires = $this->addRelation($categoires);
return $categoires;
}
public function selectChild( $id )
{
$categoires = Category::where('parent_id',$id)->where('published', 1)->paginate(40);
$categoires = $this->addRelation($categoires);
return $categoires;
}
public function addRelation( $categoires )
{
$categoires->map(function( $item, $key)
{
$sub = $this->selectChild($item->id);
$item->itemCount = $this->getItemCount($item->id , $item->parent_id );
return $item = array_add($item, 'subCategory', $sub);
});
return $categoires;
}
public function getItemCount( $category_id )
{
return Item::where('category_id', $category_id)->count();
}
This is what I have in my routes
Route::get('list/{category}', 'ListController#index')->name('list');
currently is loading urls like http://example.com/list/1 where 1 is the ID. I'm wonder if with current setup is possible to make it like, http://example.com/slug
I'm aware how slugs are working. I just can't understand how to use them in queries instead of ID's
You can use explicit Route Model Binding to grab your Category by slug before processing it.
In your RouteServiceProvider you need to bind the model:
Route::bind('category', function ($value) {
//Change slug to your column name
return App\Category::where('slug', $value)->firstOrFail();
});
Then, you can typehint the categories.
For example in your index method:
public function index(Category $category)
{
$Category = new Category;
$allCategories = $Category->getCategories();
//This line is obsolete now:
//$category = Category::find($category_id);
//...
}
Try to change your index function parameter from $category_id to $category_slug
Remove this line $Category = new Category;
And change this $category = Category::find($category_id);
To this: $category = Category::where('slug', $category_slug)->first();
*Assuming that you have a unique slug in category table

Is there a way to build a query with eloquent based off input params?

To explain this question better I'll just give an example:
Let's say I have a world records database and wish to create an api for it. Let's say I want to add a search route that takes in GET or POST parameters (let's keep it simple and just say GET for now). Is it possible to write a search controller method which uses something like an array as a parameter to Eloquent's where method while also utilizing a like parameter (MySQL LIKE)?
I have the following which works but only for exact values:
public function search()
{
$params = Input::all();
return Records::where($params)->get();
}
You should be able to:
public function search()
{
$params = Input::all();
$query = Records::newQuery();
foreach($params as $key => $value)
{
$query->where($key, 'LIKE', "%$value%")
}
return $query->get();
}
In the context of a scope, you can get fancier:
class User extends Eloquent {
public function scopeFilter($query, $input = null)
{
$input = $input ?: Input::all();
foreach($input as $key => $value)
{
if (Schema::hasColumn($this->getTable(), $key))
{
$query->where($key, 'LIKE', "%$value%")
}
}
return $query;
}
}
Then you can do:
$filtered = User::filter()->get();
Or
$filtered = User::filter(Input::only('name', 'age'))->get();

Categories