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;
}
Related
please help me, I have an AJAX search form in Laravel. It searches through the database 'patients', where they have a row 'assigned', where they are assigned to the ID of a user. The user searches through the AJAX search form, in order to find the patient. I want the user to be able to search and the results to show only the patients assigned to him, I don't want the user to be able to search and look through all the patients in the database. Here is what I tried so far, but it does not really work, please give me some ideas:
PatientController.php
function searchPatients(Request $request)
{
if($request->ajax())
{
$output = '';
$query = $request->get('query');
if($query != '')
{
if(Auth::user() -> role == 'user'){
$data = DB::table('patients')
->where('assigned', auth()->user()->id)
->where('name', 'like', '%'.$query.'%')
->orWhere('city', 'like', '%'.$query.'%')
->orWhere('country', 'like', '%'.$query.'%')
->orderBy('id', 'desc')
->get();
}
if(Auth::user() -> role == 'admin' || Auth::user() -> role == 'photostudio'){
$data = DB::table('patients')
->where('name', 'like', '%'.$query.'%')
->orWhere('city', 'like', '%'.$query.'%')
->orWhere('country', 'like', '%'.$query.'%')
->orderBy('id', 'desc')
->get();
}
}
else
{
/*$data = DB::table('patients')
->orderBy('id', 'desc')
->get();*/
}
$total_row = $data->count();
if($total_row > 0)
{
foreach($data as $row)
{
$output .= '
<div class="col-md-12 text-center offset-md-9">
<a href='.route("admin.patient", ["id" => $row->id]) .' style="text-decoration:none; color:black;">
<div class="card-mt-3">
<div id="records-patients" class="records-patients">
<div class="card-header">
Пациент: '. $row -> name. '
</div>
<div class="card-body">
<h3>Телефон:'.$row -> phone.'</h3>
<h3>Имейл: '. $row-> email.'</h3>
</div>
</div>
</div>
</a>
</div>
';
}
}
else
{
$output = '
<tr>
<td align="center" colspan="5">Nothing found, please try again</td>
</tr>
';
}
$data = array(
'table_data' => $output,
'total_data' => $total_row
);
echo json_encode($data);
}
}
AJAX search form and frontend
<script>
$(document).ready(function(){
function fetch_customer_data(query = '')
{
$.ajax({
url:"{{ route('admin.patients.search') }}",
method:'GET',
data:{query:query},
dataType:'json',
success:function(data)
{
if(query !== ''){
$('#patientsshow').html(data.table_data);
$('#total_records').text(data.total_data);
}
}
})
}
$(document).on('keyup', '#search-patients', function(){
var query = $(this).val();
if(query !== ''){
fetch_customer_data(query);
}
});
});
</script>
<div class="row">
<div id="patientsshow">
</div>
</div>
You can define relationship between User and Patient models
class User extends Model
{
public function patients()
{
return $this->hasMany(Patient::class, 'assigned', 'id');
}
}
class Patient extends Model
{
public function user()
{
return $this->belongsTo(User::class, 'assigned', 'id');
}
}
Then in the controller you can just get the patients for the currently logged in user using the relationship
class PatientController extends Controller
{
public function index(Request $request)
{
$user = $request->user();
//or
$user = auth()->user();
$patients = $user->isAdmin ? Patient::all() : $user->patients;
//do other stuff with the collection and return response
}
}
Made this work by this one in PatientController.php
if(Auth::user()-> role == 'user'){
$data = Patient::where('assigned', auth()->user()->id)
->where('name', 'like', '%'.$query.'%')
->get();
}
I am working on a project for ads/properties in Laravel. I have gallery of multiple videos for each individual ad. I want to be able to select one of those videos with radio button and make it primary video for that ad (show that particular video next to ad). I have has many relationship between property and video. I am trying to update my is_main_video column to be 1 if it is main or 0 if it isn't and then display that video if it is 1 in my view. I am having trouble to write that method in my controller I get success message but my is_main_video column remains null. Any help is appreciated. Here are my tables and code.
properties (id, title, location, price)
videos (id, filename_video, model_id, is_main_video)
In videos table model_id column is foreign key that connects to properties table.
PropertyController.php
public function update(StorePropertyInfo $request, Property $property, Video $video)
{
$videoExtensions = ['mp4', '3gp', 'wmv', 'flv', 'avi'];
$image_arr = array_filter(explode(',', $request->image), 'strlen');
foreach ($image_arr as $key => $value) {
$file = explode('.', $value);
ext = end($file);
if (in_array($ext, $videoExtensions)) {
$query = Property::query();
if ($request->has('radio')) {
$request->get('radio');
}
if ($request->radio) {
$query->whereHas('videos', function ($query) use ($request) {
$query->where('is_main_video', 'like', '%' . $request->radio . '%');
});
}
$data = $query;
$video->where('filename_video', $value)
->update([
'model_id' => $property->id,
'is_main_video' => $data
]);
};
$request->validated();
return redirect()
->back()
->with('message', 'Property information updated');
}
}
edit.blade.php
<input type="hidden" id="hideninput" data-src="property/{{$property->id}}/gallery"value="{{old('video',$video)}}" name="video">
#if (old('video', $video))
#foreach (array_filter(explode(',',old('video', $video)), 'strlen') as $key =>$value)
<div id="{{'div_video_'.$key}}" class="col-md-3" style="padding: 15px;">
<input type="radio" name="radio" value="radio">Make main
<button data="{{$key}}" type="button" class="closebuttonvideo">
<span aria-hidden="true">×</span>
</button>
<video name="video" id="{{'video_'.$key}}" data={{$value}} src="/storage/property/{{$property->id}}/gallery/{{$value}}" class="video-js vjs-default-skin" controls preload="auto" data-setup='{"inactivityTimeout": 0}' width="180" height="180"></video>
</div>
#endforeach
#endif
videos table migration
public function up()
{
Schema::table('videos', function (Blueprint $table) {
$table->boolean('is_main_video')->nullable()->default(null);
$table->unique(['model_id', 'is_main_video']);
});
}
Property.php
protected $appends = ['is_main_video'];
public function videos()
{
return $this->hasMany(Video::class, 'model_id');
}
Video.php
public function properties()
{
return $this->belongsTo(Property::class);
}
I would do a partial rewrite, something like this:
PropertyController.php in edit(); Retrieving a property with attached videos.
return view('edit', [
'property' => Property::with('videos')->find(1),
]);
edit.blade.php
#foreach($property->videos as $video)
<input type="radio" name="main_video" value="{{ $video->id }}">Video {{ $video->id }}<br>
#endforeach
PropertyController.php in update(); Toggle the is_main_video attribute of each video of the property.
foreach ($property->videos as $video) {
$video->is_main_video = ($video->id == $request->input('main_video'));
$video->save();
}
I have search form to list properties/ads through certain criteria. In my form I am trying to search and list properties from database that correspond to entered min_price and max_price. When I submit form I get no results in table and when I die and dump min_price or max_price variable I get false. Any help is appreciated. Here is my code
CategoryController.php
<?php
namespace App\Http\Controllers;
use App\Category;
use App\Property;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Redirect;
class CategoryController extends Controller
{
public function index()
{
return view('categories.search', compact('data'));
}
public function search($price, Request $request, Property $property)
{
$category = $property->category;
$query = Property::query();
// Code for min and max price
$min_price = $request->has('min_price');
$max_price = $request->has('max_price');
//dd($max_price);
if (($min_price) && ($max_price)) {
$query->whereBetween('price', [$min_price, $max_price]);
}
elseif (! is_null($min_price)) {
$query->where('price', '>=', $min_price);
}
elseif (! is_null($max_price)) {
$query->where('price', '<=', $max_price);
}
$results = $query->get();
return view('categories.search', compact('category', 'results'));
}
}
search.blade.php
#if(isset($results))
<table class="table">
<thead>
<th>Price</th>
</thead>
<tbody>
#foreach ($results as $result)
<tr>
<td>{{ $result->price }}</td>
</tr>
#endforeach
</tbody>
</table>
#endif
<form id="searchForm" method="GET" action="/search">
<div class="col-md-6 mb-6">
<label>Price</label>
<input type="number" id="min_price" name="min_price" class="form-control" placeholder="Min Price">
<input type="number" id="max_price" name="max_price" class="form-control" placeholder="Max Price">
</div>
<button class="btn btn-primary btn-lg btn-block">Search</button>
</form>
Main problem was is because you didn't have data in $request but in your variable $price you had something like this: "min_price=1000-max_price=2000".
So you need to get values from that string:
public function search($price, Request $request, Property $property)
{
$category = $property->category;
$array = explode('-', $price); // Array of your values
foreach($array as $a){
$s = [];
$s = explode('=', $a);
if ($s[0] === 'min_price'){
$s[1] ? $min_price = intval($s[1]) : $min_price = null;
} else {
$s[1] ? $max_price = intval($s[1]) : $max_price = null;
}
}
if (!empty($min_price) && !empty($max_price)) {
$results = Property::whereBetween('price', [$min_price, $max_price])->get();
return view('categories.search', compact('category', 'results'));
}
elseif (!empty($min_price)) {
$results = Property::where('price', '>=', $min_price)->get();
}
elseif (!empty($max_price)) {
$results = Property::where('price', '<=', $max_price)->get();
}
return view('categories.search', compact('category', 'results'));
}
Maybe you need to try to send min_price and max_price in better format, but this should work.
Good luck.
You can use laravel elqouent when method do do this.
So your query should look something like the following
$query = Property::query();
$request->has('max_price') && $request->has('min_price')
? $query->whereBetween('price', [$request->get('min_price'), $request->get('max_price')])
: $query->when($request->has('min_price'), function ($query) use ($request) {
$query->where('price', '>=', $request->get('min_price'));
})->when($request->has('min_price'), function ($query) use ($request) {
$query->where('price', '<=', $request->get('max_price'));
});
$results = $query->get();
Have a look at this https://laraveldaily.com/less-know-way-conditional-queries/ for more details
Good Luck
$request->has is a laravel function just to check if your request has certain value or not it will always return true or false.While to get request value you can use $request->get('name') so your search function should be:
public function search($price, Request $request, Property $property)
{
$category = $property->category;
$query = Property::query();
// Code for min and max price
$min_price= 0;
$max_price= 0;
if($request->has('min_price')){
$min_price = $request->get('min_price');
}
if($request->has('max_price')){
$max_price = $request->get('max_price');
}
//dd($max_price);
if (($min_price) && ($max_price)) {
$query->whereBetween('price', [$min_price, $max_price]);
}
elseif (! is_null($min_price)) {
$query->where('price', '>=', $min_price);
}
elseif (! is_null($max_price)) {
$query->where('price', '<=', $max_price);
}
$results = $query->get();
return view('categories.search', compact('category', 'results'));
}
}
I am trying to search by check-boxes and conditions but i get this error in return:
count(): Parameter must be an array or an object that implements Countable
Logic
I can select 1 or more options
In back-end check what kind of options has selected
Depend of the kind return results
Code
controller
public function advancedsearch(Request $request) {
$options = Specification::whereHas('subspecifications')->with(['subspecifications' => function($query) {
$query->status('Active');
}])->get();
$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_subspecification', function ($join) {
$suboption = Input::has('suboptions') ? Input::get('suboptions') : [];
$join->on('products.id', '=', 'product_subspecification.product_id')
->where('product_subspecification.subspecification_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'));
}
Summary of code above
I am getting all brands from database and check if only 1 brand has selected or several
$brands = Brand::all();
$brandss = Input::has('brands') ? Input::get('brands') : [];
Then I check if any brand has selected in general or no by count and returning results
elseif(count($request['brands'])){
$products = DB::table('products')
->whereIn('products.brand_id', $brandss)
->paginate(12);
}
but I get error I share above.
Any idea?
Update
how my html look likes
how the result **dd** look like
array:4 [▼
"_token" => "Wqs9yzd5qwGtbv01asbCzsISeVQxHsCoWVQM1ifO"
"brands" => array:1 [▼
0 => "3"
]
"min_price" => null
"max_price" => null
]
Blade
<div class="checkbox">
#foreach($option->subspecifications as $suboption)
#if($option->title == 'Brand')
<label for="brands">
<input name="brands[]" type="checkbox" value="{{$suboption->id}}">
{{ucfirst($suboption->title)}}
</label>
#else
<label for="suboptions">
<input name="suboptions[]" type="checkbox" value="{{$suboption->id}}">
{{ucfirst($suboption->title)}}
</label>
#endif
#endforeach
</div>
Solved
Changed count with has solved the issue
elseif($request->has['brands']){
PS: Based on 3 different functions that I have, which returns data of my search I have mixed results but it doesn't related to this
question. count error solved so I'll mark it as solved.
Thanks to miken32
We recently had a contractor in to do some work on our app, and now I'm having to debug it. I'm still quite new to Laravel and I'm struggling to debug the problem. They've left for newer pastures and aren't available to help.
What should be happening is a printout of only the matched results. So let's say we have an Agency id of 141 and they have 1 preferred region, South East Asia.
Let's take user 252 and we find they have multiple preferred regions, 1 of which is South East Asia. I only want to return the matched region.
Currently this is not happening.
Currently, out of user 252's preferences:
No Preference - Any Region
Eastern Europe
We're getting a result of:
Eastern Europe
South East Asia
My assumption is that this section of code here is the culprit? SQL isn't my strong point.
/**
* Match regions with 2 given id's
*
* #return Array with the matched regions_id
* #author
**/
public function matchRegions ($itemOne, $itemTwo)
{
return \App\Regionable::selectRaw('count(*) AS cnt, regions_id, description')
->join('regions', 'regions_id', '=', 'regions.id')
->where('regionable_id', $itemOne)
->orWhere('regionable_id', $itemTwo)
->groupBy('regions_id')
->havingRaw('cnt > 1')
->get();
}
public function matchPreferences ($itemOne, $itemTwo)
{
return \App\Preferable::selectRaw('count(*) AS cnt, ministry__prefs_id, description')
->join('ministry_prefs', 'ministry__prefs_id', '=', 'ministry_prefs.id')
->where('preferable_id', $itemOne)
->orWhere('preferable_id', $itemTwo)
->groupBy('ministry__prefs_id')
->havingRaw('cnt > 1')
->get();
}
public function matchLanguages($itemOne, $itemTwo)
{
return \App\Languageable::selectRaw('count(*) AS cnt, language_id, subcategory as description')
->join('languages', 'language_id', '=', 'languages.id')
->where('languageable_id', $itemOne)
->orWhere('languageable_id', $itemTwo)
->groupBy('language_id')
->havingRaw('cnt > 1')
->get();
}
public function matchCommitment($itemOne, $itemTwo)
{
return \App\Timeable::selectRaw('count(*) AS cnt, time__commitment_id, timecommit.description')
->join('timecommit', 'time__commitment_id', '=', 'timecommit.id')
->where('timeable_id', $itemOne)
->orWhere('timeable_id', $itemTwo)
->groupBy('time__commitment_id')
->havingRaw('cnt > 1')
->get();
}
public function prefCount ()
{
return (count($this->regions()->get())+count($this->ministry_prefs()->get())+count($this->languages()->get())+count($this->time_commitment()->get()));
}
public function matchSeeker($userIds = [])
{
if(empty($userIds)) $userIds = UserInfo::all()->pluck('id');
$agencyPrefCount = $this->prefCount();
foreach ($userIds as $userId) {
$seekerPrefCount = 0;
$seeker = [];
$regions = $this->matchRegions($this->id, $userId);
$preferences = $this->matchPreferences($this->id, $userId);
$languages = $this->matchLanguages($this->id, $userId);
$timecommit = $this->matchCommitment($this->id, $userId);
if(count($regions) > 0) $seeker['regions'] = $regions;
if(count($preferences) > 0) $seeker['preferences'] = $preferences;
if(count($languages) > 0) $seeker['languages'] = $languages;
if(count($timecommit) > 0) $seeker['timecommit'] = $timecommit;
if(!empty($seeker)) {
$seeker['details'] = UserInfo::findOrFail($userId);
$seekerPrefCount = (count($regions)+count($preferences)+count($languages)+count($timecommit));
if(($agencyPrefCount == 0) || ($seekerPrefCount == 0)) $seeker['relevance'] = 0;
else $seeker['relevance'] = round((($seekerPrefCount / $agencyPrefCount) * 100),2);
$seekers[$userId] = $seeker;
}
}
$seekers = array_reverse(array_values(array_sort($seekers, function ($value) {
return $value['relevance'];
})));
return $seekers;
}
Printing out the results to the view works something like so:
Controller:
public function getDashboard() {
$agency = Agency::with('ministry_prefs', 'languages', 'time_commitment', 'regions')->findOrFail(Auth::user()->login_id);
$seekers = $agency->matchSeeker();
return view('agency-area.home', compact('agency', 'seekers'));
}
Blade:
#foreach($seekers as $seekerId => $seeker)
<?php
switch (true) {
case ($seeker['relevance'] <= 20) :
$level = 'bg-red';
break;
case (($seeker['relevance'] >= 21) && ($seeker['relevance'] <= 30)):
$level = 'bg-orange';
break;
default:
$level = 'bg-green';
}
?>
<div class="box-comment">
{{ Html::image('img/avatar.jpg', 'User Image', array('class' => 'img-circle img-sm')) }}<!-- User image -->
{{-- <button type="button" class="btn btn-default btn-xs"><i class="fa fa-thumbs-o-up"></i> Like</button> --}}
<div class="comment-text">
<span class="username">
{{((!empty($seeker['details']->name)) ? $seeker['details']->name : $seeker['details']->First_Name." ".$seeker['details']->Last_Name)}} {{$seeker['details']->First_Name." ".$seeker['details']->Last_Name}}
<span class="text-muted pull-right">{{$seeker['details']->created_at}}</span>
{{-- <span class="badge {{$level}}">{{$seeker['relevance']}} % <span class="text-white">Match</span></span> --}}
</span><!-- /.username -->
{{$seeker['details']->Experience}}
<hr/>
<div class="row">
#if(!empty($seeker["regions"]))
<div class="col-xs-3">
<h4>Regions</h4>
#foreach($seeker["regions"] as $region)
<small>{{$region->description}}</small><br/>
#endforeach
</div>
#endif
#if(!empty($seeker["preferences"]))
<div class="col-xs-3">
<h4>Preferences</h4>
#foreach($seeker["preferences"] as $preferences)
<small>{{$preferences->description}}</small><br/>
#endforeach
</div>
#endif
#if(!empty($seeker["languages"]))
<div class="col-xs-3">
<h4>Languages</h4>
#foreach($seeker["languages"] as $commitment)
<small>{{$commitment->description}}</small><br/>
#endforeach
</div>
#endif
#if(!empty($seeker["timecommit"]))
<div class="col-xs-3">
<h4>Commitment</h4>
#foreach($seeker["timecommit"] as $commitment)
<small>{{$commitment->description}}</small><br/>
{{-- {{$commitment->time__commitment_id}} --}}
#endforeach
</div>
#endif
</div>
</div> <!-- /.comment-text -->
</div><!-- /.box-comment -->
#endforeach
Ok, many thanks to #Antonis-Tsimourtos for pointing me in the right direction with debugging.
Putting down my solution at the moment (note I haven't tested how many queries this is making. This solves the problem, but it's innefficient I think).
The issue arises from the joining statement the contractor used:
public function matchLanguages($itemOne, $itemTwo)
{
return \App\Languageable::selectRaw('count(*) AS cnt, language_id, subcategory as description')
->join('languages', 'language_id', '=', 'languages.id')
->where('languageable_id', $itemOne)
->orWhere('languageable_id', $itemTwo)
->groupBy('language_id')
->havingRaw('cnt > 1')
->get();
}
ItemOne and Two are the user ids of the accounts being matched against. If you look at the join, it's comparing them, but instead of filtering out the final result, it's tacking on each result with the where and orwhere. Those who use laravel will recognise this is also unnecessary as Laravel's relationships take care of half the query being made here.
The solution is to use those queries, like so:
public function matchLanguages($itemOne, $itemTwo)
{
$languages1 = \App\UserInfo::find($itemOne)->languages()->get();
$languages2 = \App\Agency::find($itemTwo)->languages()->get();
return $languages1->intersect($languages2);
}
Unfortunately I'm not eager loading these relationships, which is probably the next step in optimisation. But that requires hunting through the code a bit.
Hope that helps!