I'm trying to filter products based on query string. My goal is to get products from a collection if it's given, otherwise get every product. Could someone help me what's wrong with the following code?
$products = \App\Product::where([
'collection' => (request()->has('collection')) ? request('collection') : '[a-z]+',
'type' => (request()->has('type')) ? request('type') : '[a-z]+'
])->get();
PS.: I've also tried with 'regex:/[a-z]+', it's not working...
$products = \App\Product::where(['collection' => (request()->has('collection')) ? request('collection') : 'regex:/[a-z]+'])->get();
What you can do is use when eloquent clause, so your where clause for collections will be triggered only when the request('collection') exists, same logis applie to type as well.
$products = \App\Product::
when(request()->has('collection'), function ($q) {
return $q->where('collection', request('collection'));
});
->when(request()->has('type'), function ($q) {
return $q->where('type', request('type'));
})
->get();
Or another way if you have your request values assigned to a variable something like:
$collection = request('collection');
$type= request('type');
$products = \App\Product::
when(!empty($collection), function ($q) use ($collection) {
return $q->where('collection', $collection);
});
->when(!empty($type), function ($q) use ($type) {
return $q->where('type', $type);
})
->get();
Hi i'm trying to make a small system filtering in laravel using the following function in my controller to filter users by their roles or their names here's my current code:
profilecontroller.php:
public function membrevis()
{
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
$users = DB::table('users')
->join('user_role', 'users.id', '=', 'user_role.user_id')
->join('roles', 'user_role.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->select('users.*','roles.description');
if ($filter != null) {
$users->where('users.name','like','%'.$filter.'%')
->orWhere('roles.description',' like','%'.$filter.'%');
}
$users->get();
return view('membre2',['users'=> $users]);
}
My view has an input form where you can type the name or the role of the members you are trying to filter :
membre2.blade.php:
<form action="/profilecontroller/membrevis" method="get">
<input type="text" name="filter" >
<button type="submit">filter</button>
</form>
#foreach($users as $users)
<h4 class="media-heading">{{ $users->name }}</h4>
#endforeach
The error i'm getting is Undefined property: Illuminate\Database\MySqlConnection::$name
I have no idea why i'm getting this error i'm obviously passing $users to my view with this line of command:
return view('membre2',['users'=> $users]);
Any help would be appreciated! thank you
when using $users->get() it returns Collection instance, you must be assign $users = $users->get(); Corrected code is
public function membrevis()
{
$filter = isset($_GET['filter']) ? $_GET['filter'] : null;
// also can use this structure $filter = isset($_GET['filter']) ?? null;
$users = DB::table('users')
->join('user_role', 'users.id', '=', 'user_role.user_id')
->join('roles', 'user_role.role_id', '=', 'roles.id')
->where('users.valid','=',0)
->select('users.*','roles.description');
if ($filter != null) {
$users->where('users.name','like','%'.$filter.'%')
->orWhere('roles.description',' like','%'.$filter.'%');
}
$users = $users->get();
return view('membre2',['users'=> $users]);
}
also in blade fix it
#foreach($users as $user)
<h4 class="media-heading">{{ $user->name }}</h4>
#endforeach
I have a search form with multiple input and select boxes I need help to get if conditions in my query in order to each part works separately and all at once.
here is my blade codes:
<form action="{{route('advancesearch')}}" method="post">
{{csrf_field()}}
<div class="sidebar-title">
<span>Advanced Search</span>
<i class="fa fa-caret-down show_sidebar_content" aria-hidden="true"></i>
</div>
<!-- ./sidebar-title -->
<div id="tags-filter-content" class="sidebar-content">
<div class="filter-tag-group">
#foreach($options as $option)
<div class="tag-group">
<p class="title">
<span class="filter-title show_filter_content">{{$option->title}} <span class="pull-right"><i class="fa fa-minus"></i></span></span>
</p>
<div class="filter-content">
<div class="checkbox">
#foreach($option->suboptions as $suboption)
<label for="suboptions">
<input name="suboptions[]" type="checkbox" value="{{$suboption->id}}">
{{ucfirst($suboption->title)}}
</label>
#endforeach
</div>
</div>
</div>
#endforeach
<!-- ./tag-group -->
<div class="tag-group">
<p class="title">
<span class="filter-title show_filter_content">Brand <span class="pull-right"><i class="fa fa-minus"></i></span></span>
</p>
<div class="filter-content">
<div class="checkbox">
#foreach($brands as $brand)
<label for="brands">
<input name="brands[]" type="checkbox" value="{{$brand->id}}">
{{$brand->title}}
</label>
#endforeach
</div>
</div>
</div>
<!-- ./tag-group -->
<div class="tag-group">
<p class="title">
<span class="filter-title show_filter_content">Price Range <span class="pull-right"><i class="fa fa-minus"></i></span></span>
</p>
<div class="row filter-content">
<div class="col-md-6">
<div class="form-group">
<label for="min_price" hidden>Min</label>
<input type="text" name="min_price" class="form-control" placeholder="Rp Min">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="max_price" hidden>Max</label>
<input type="text" name="max_price" class="form-control" placeholder="Rp Max">
</div>
</div>
</div>
</div>
<!-- tag-group -->
<div class="text-center mt-20">
<button type="submit" class="btn btn-danger">TERPAKAN</button>
</div>
</div><!-- ./filter-tag-group -->
</div><!-- ./sidebar-content -->
</form>
and this is my route:
Route::post('/advanced-search', 'frontend\SearchController#filter')->name('advancesearch');
finally my function code is:
public function advancedsearch(Request $request) {
$brands = Brand::all(); // uses for other part of the page. (not related to search function)
$options = Option::all(); // uses for other part of the page. (not related to search function)
$suboptions = DB::table('product_suboption'); // where my product_id and subopyion_id saves
//search function
$products = Product::where(function($query){
//getting inputs
$suboptions2 = Input::has('suboptions') ? Input::get('suboptions') : [];
$min_price = Input::has('min_price') ? Input::get('min_price') : null;
$max_price = Input::has('max_price') ? Input::get('max_price') : null;
$brands2 = Input::has('brands') ? Input::get('brands') : [];
//returning results
$query->where('price','>=',$min_price)
->where('price','<=',$max_price);
})->get();
return view('front.advancesearch', compact('products', 'brands', 'options'));
}
My models relations:
product model:
public function options(){
return $this->belongsToMany(Option::class);
}
public function suboptions(){
return $this->belongsToMany(Suboption::class, 'product_suboption', 'product_id', 'suboption_id');
}
public function brand(){
return $this->belongsTo(Brand::class);
}
Option model:
public function suboptions(){
return $this->hasMany(Suboption::class, 'option_id');
}
public function products(){
return $this->belongsToMany(Product::class);
}
Suboption model:
public function option(){
return $this->belongsTo(Option::class, 'option_id');
}
public function products(){
return $this->belongsToMany(Product::class);
}
Brand model:
public function products(){
return $this->hasMany(Product::class);
}
note
My brands search is coming from products table where I have column brand_id for each product.
BUT
My suboptions come from 3rd table named product_suboption (as you see in my models codes) where i save product_id and suboption_id.
This is just to give an idea. You can use a multiple ->where() and eager loading ->with() for your query.
Take a look with this query below:
$products = Product::where('price', '>=', $min_price) // you get the max and min price
->where('id', '<=', $max_price)->select('id')
->with([
"brand" => function ($query) {
$query->whereIn('id', $brand_ids); // [1, 2, 3,...]
},
"specifications" => function ($query) {
$query->where('some_column', '=', 'possible-value'); // single condition
},
"specifications.subspecifications" => function ($query) {
$query->where([
'some_column' => 'possible-value',
'another_column' => 'possible-value'
]); // you can also pass arrays of condition
}
])->get(); // This will return the products with the price set by the user
// Since we're just using ->with(), this will also return those products
// that doesn't match the other criteria specifications) so we
// still need to filter it.
Finally, you can filter the products which matches the specifications,
- the product with an empty specifications means this product does not match the criteria, therefore we'll have to remove it from the collection.
$filtered = $products->filter(function ($product, $key) {
return count($product->brand) > 0 && count($product->specifications) > 0;
// add your other boolean conditions here
});
dd($filtered->toArray()); // your filtered products to return
You can use laravel orWhere and orWhereHas to get results separately and all at once, let's say you do not select min_price and max_price but you have selected brand then all products with this brnad should be return, your query will look like this
$products = Product::orWhere('price','>=',$min_price)
->orWhere('price','<=',$max_price)
->orWhereHas('brand',function($query){
$query->whereIn('id', $brand_ids);
})
->orWhereHas('suboptions',function($query){
$query->whereIn('id', $suboptions_ids);
})
->orWhereHas('subspecifications',function($query){
$query->whereIn('id', $subspecifications_ids);
})->get();
$products will have products collection If any of the condition stated in above query matched.
Hope this helps.
Here's how I'd do it. Note the use of when for simplifying optional where conditions (no need to set variables either), and the closure for constraining both the whereHas and the with (if you want to eager load the relationships).
$products = Product::query()
->when($request->min_price, function ($query, $min_price) {
return $query->where('price', '>=', $min_price);
})
->when($request->max_price, function ($query, $max_price) {
return $query->where('price', '<=', $max_price);
})
->when($request->suboptions, function ($query, $suboptions) {
$suboptionsConstraint = function ($q) use ($suboptions) {
return $q->whereIn('id', $suboptions);
};
return $query->whereHas('suboptions', $suboptionsContraint)
->with(['suboptions' => $suboptionsContraint]);
})
->when($request->brands, function ($query, $brands) {
$brandsConstraint = function ($q) use ($brands) {
return $q->whereIn('id', $brands);
};
return $query->whereHas('brands', $brandsConstraint)
->with(['brands' => $brandsConstraint]);
});
I suggest tu use each separeted and its help you to feature easaly manupulate code
as your typical condition your sub_option come from third table last relation ship is used.
if(count($request['suboptions'])) {
$product->whereHas('options',function($options) use ($request) {
$options->whereHas('suboptions',function($suboption)use($request) {
$suboption->whereIn('id',$request['suboptions']);
});
});
}
for min price max price i assume your price in procuct table
if(! empty($request['min_price'])) {
$product->where('price','>=',$request['min_price']);
}
if(! empty($request['max_price'])) {
$product->where('price','<=',$request['max_price']);
}
for brand as you say brand_id column in product table then
if(count($request['brands'])) {
$product->whereIn('brand_id',$request['brands']);
}
I suggest a different approach.
On your controller, change it to this:
public function advancedsearch(Request $request) {
$suboptions2 = request->suboptions ? request->suboptions : null;
$min_price = request->min_price ? request->min_price : null;
$max_price = request->max_price ? request->max_price : null;
$brands2 = request->brands ? request->brands : null;
$query = Product::select('field_1', 'field_2', 'field_3')
->join('brands as b', 'b.id', '=', 'products.brand_id')
...(others joins);
// here we do the search query
if($suboptions2){
$query->where('suboptions_field', '=', $suboptions);
}
if($min_price && $max_price){
$query->where(function($q2) {
$q2->where('price', '>=', $min_price)
->where('price', '<=', $max_price)
});
}
if($brands2){
$query->where('products.brand_id', '=', $brands2);
}
// others queries
// finish it with this
$query->get();
return view('front.advancesearch', compact('products', 'brands', 'options'));
I find doing it this way is very useful because it can be really easy to implement additional queries.
This is the method I use to search using laravel eloquent with multiple input:
$input = Input::all(); //group all the inputs into single array
$product = Product::with('options','suboptions','brand');
//looping through your input to filter your product result
foreach ($input as $key => $value)
{
if ($value!='') {
if ($key == "max_price")
$product = $product->where('price','<=', $value);
elseif ($key == "min_price")
$product = $product->where('price','>=', $value);
elseif ($key == "brands")
$product = $product->whereIn('brand_id', $value); //assuming that your Input::get('brands') is in array format
elseif ($key == "suboptions")
$product = $product->whereIn('suboption_id', $value);
}
}
$product = $product->get();
The method above will return all products if no input is submitted, and will filter the result based on the input if available, on top of this it's also a good practice to sanitize your inputs with validations before proceeding with the query
SOLVED
After weeks of playing with codes finally I came to the right results for myself (in my case it works this way for others maybe works with other suggested answers)
public function advancedsearch(Request $request) {
$options = Option::all();
$brands = Brand::all();
$brandss = Input::has('brands') ? Input::get('brands') : [];
$suboption = Input::has('suboptions') ? (int)Input::get('suboptions') : [];
$min_price = Input::has('min_price') ? (int)Input::get('min_price') : null;
$max_price = Input::has('max_price') ? (int)Input::get('max_price') : null;
//codes
if(count($request['suboptions'])){
$products = DB::table('products')
->join('product_suboption', function ($join) {
$suboption = Input::has('suboptions') ? Input::get('suboptions') : [];
$join->on('products.id', '=', 'product_suboption.product_id')
->where('product_suboption.suboption_id', '=', $suboption);
})
->paginate(12);
}
elseif(count($request['brands'])){
$products = DB::table('products')
->whereIn('products.brand_id', $brandss)
->paginate(12);
}
elseif(count($request['min_price']) && count($request['max_price'])){
$products = DB::table('products')
->whereBetween('price', [$min_price, $max_price])
->paginate(12);
}
return view('front.advancesearch', compact('products', 'brands', 'options'));
}
NOTE: most of my pricing issues solved with (int) as you see in my codes (int)Input::get('min_price') and
(int)Input::get('max_price').
Special thanks to Ravindra Bhanderi for his count($request[''] suggestion.
it is very simple to make dynamic search by using treats we can use this for all models I made this dynamic as possible
This is a trait that can be used by any models
This function will remove code repetitions into your project
public function scopeSearch($query, $keyword, $columns = [], $relativeTables = [])
{
if (empty($columns)) {
$columns = array_except(
Schema::getColumnListing($this->table), $this->guarded
);
}
$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);
}
}
});
return $query;
}
Filter into relationship also
private function filterByRelationship($query, $keyword, $relativeTables)
{
foreach ($relativeTables as $relationship => $relativeColumns) {
$query->orWhereHas($relationship, function($relationQuery) use ($keyword, $relativeColumns) {
foreach ($relativeColumns as $key => $column) {
$clause = $key == 0 ? 'where' : 'orWhere';
$relationQuery->$clause($column, "LIKE", "%$keyword%");
}
});
}
return $query;
}
I save my order data to notifications table as my notification using laravel Notification and I have problem with returning my data.
here is my notification method: App\Notifications\OrderSubmited.php
public function toArray($notifiable)
{
return [
'order_id' => $this->order->id,
'title' => $this->order->title,
'buyer' => $this->order->user_id,
'status' => $this->order->orderstatus_id,
];
}
My method to get data:
View::composer('admin.dashboard', function ($view) {
$notifications = DB::table('notifications')->orderby('id', 'desc')
->latest()
->take(5)
->get();
$notifs = $notifications;
$view->with('notifs', $notifs);
});
here is my blade code:
#foreach($notifs as $notif)
{{dd($notif)}}
#foreach($notif->data as $data)
{{$data['order_id']}} <br>
#endforeach
#endforeach
this is my {{dd($notif)}} output:
{#721 ▼
+"id": "46546547464415474884"
+"type": "App\Notifications\OrderSubmited"
+"notifiable_id": 2
+"notifiable_type": "App\Order"
+"data": "{"order_id":2,"title":"tsdfg", "user_id":"1", "orderstatus_id":"11"}"
+"read_at": null
+"created_at": "2018-01-18 00:00:00"
+"updated_at": "2018-01-18 00:00:00"
}
as you can see data i try to return are stored in data column.
If I remove my dd i will get this error:
Invalid argument supplied for foreach()
On this line:
#foreach($notif->data as $data)
Any idea?
Each notification is an object containing an array called data containing the values you return from your toArray method when creating the notification. You would normally iterate through your notifications and access their data like so:
#foreach ($notifications as $notification)
{{ $notification->data['order_id'] }}
#endforeach
Laravel converts the data property (which is stored in the database as JSON) into an array.
However because you are retrieving the notifications yourself from the database without making use of the built in methods you will need to convert that data to an array from JSON yourself, like so:
View::composer('admin.dashboard', function ($view) {
$notifications = DB::table('notifications')->orderby('id', 'desc')
->latest()
->take(5)
->get();
$view->with('notifications', $notifications);
});
Then from within your view:
#foreach ($notifications as $notification)
#php $data = json_decode($notification->data, true); #endphp
{{ $data['order_id'] }}
#endforeach
Regarding your follow up question, something like this:
View::composer('admin.dashboard', function ($view) {
$notifications = DB::table('notifications')->orderby('id', 'desc')
->latest()
->take(5)
->get()
->map(function ($item, $key) {
$item->data = json_decode($item->data);
$item->data->user = User::find($item->data->user_id);
return $item;
});
$view->with('notifications', $notifications);
});
Then from within your view:
#foreach ($notifications as $notification)
{{ $notification->data->user->name }}
#endforeach
I'm creating an edit page of a group-making of an organization. each group consists of a mentor and some mentees.
The problem is i can't iterate using the result because it is a collection of a collection and a user, instead of giving a (directly) collection of user.
the error is this
ErrorException in dd3280396f5fc9a234b8faac1b17b55bd5c30ae2.php line 77:
Undefined property: Illuminate\Database\Eloquent\Collection::$id (View: E:\Documents\Karya\simentor\resources\views\group\edit.blade.php)
Here's the result of dd($mentees)
Collection {#260 ▼
#items: array:2 [▼
0 => Collection {#286 ▼
#items: array:1 [▼
0 => User {#284 ▶}
]
}
1 => User {#261 ▶}
]
}
To get all mentors and mentees who don't have any group yet and to get the mentor and mentees of the to-be-edited group, i did this in GroupController.php
public function edit($id)
{
$group = Group::find($id);
//get mentor and mentee who don't have group yet
$unassignedMentors = User::whereHas(
'roles', function ($q) {
$q->where('name', 'mentor');
})
->doesntHave('groups')
->get();
$unassignedMentees = User::whereHas(
'roles', function ($q) {
$q->where('name', 'mentee');
})
->doesntHave('groups')
->get();
//get mentor and mentee of the to-be-edited group
$assignedMentor = $group->mentor();
$assignedMentee = $group->mentee();
//combine those mentors and mentees into a collection
$mentors = $unassignedMentors->prepend($assignedMentor);
$mentees = $unassignedMentees->prepend($assignedMentee);
return view('group.edit', compact('group', 'mentees', 'mentors', 'assignedMentor', 'assignedMentee'));
}
The mentor() and mentee() method defined in Group.php like this
public function mentor()
{
return $this->users()->whereHas(
'roles', function($q){
$q->where('name', 'mentor');
})
->first();
}
public function mentee()
{
return $this->users()->whereHas(
'roles', function($q){
$q->where('name', 'mentee');
})
->get();
}
Here's the code to display the checkboxes and names in edit.blade.php
#foreach ($mentees as $mentee)
<tr>
<td><input type="checkbox" name="mentee_id[]" value="{{$mentee->id}}" {{ array_has($assignedMentee, $mentee) ? 'checked' : '' }}></td> --> line 77
<td>{{ucwords($mentee->profile->name)}}</td>
</tr>
#endforeach