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;
}
Related
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);
}
public function scopeSearch($query, $value)
{
$searchValues = explode(' ', $value);
if (!$value) return $query;
return $query->where(function ($q) use ($searchValues) {
foreach ($searchValues as $token) {
$q->orWhere('name', 'like', "%{$token}%");
$q->orWhere('street', 'like', "%{$token}%");
}
});
}
I want to search the data. This model also has
public function brands()
{
return $this->belongsToMany(Brand::class, 'dealer_brands');
}
public function province()
{
return $this->belongsTo(Province::class);
}
How can I get data from the relastionship. Like Dealer(model) has data Nmae = josh , brand_id = 1 {brand.name = samsung} , province_id = 2 (province.name = "aligora"). When I search Josh Samsung Alogora, I want to ge the data. When I only search aligora, I want to get the data of model having province aligora. hOW CAN I MODIFY CODE?
looking at your model relationships. This may work for you
public function scopeSearch($query, $value)
{
if (!$value) return $query;
$searchValues = explode(' ', $value);
return $query->where(function ($q) use ($searchValues) {
foreach ($searchValues as $token) {
$q->where('name', 'like', "%{$token}%")
->orWhere('street', 'like', "%{$token}%")
->orWhereHas('brands', function ($sub_q) use ($searchValues) {
$sub_q->where('name', 'like', "%{$token}%")
->orWhere('street', 'like', "%{$token}%");
})
->orWhereHas('province', function ($sub_q) use ($searchValues) {
$sub_q->where('name', 'like', "%{$token}%")
->orWhere('street', 'like', "%{$token}%");
});
}
});
}
I have no idea about column names in related tables so repeating name and street. you can change as per requirement
I have been trying to search in both the name column and that's relationship. I have coded as below;
///
$posts = ContentForSearch::with("content_type","content_sub_type")->exclude($exclude)->user($user_id_filter)
->where('name', 'LIKE', "%{$search}%")
->orWhereHas('tags', function ($q) use ($search) {
$q->where('tag', 'LIKE', "%{$search}%");
})
->orderBy($sort, $order)
->paginate($limit);
I have set a searchable Model. I want to change with search method as below;
$posts = ContentForSearch::search($search)->orWhere(function ($query) use ($search) {
$query->whereHas('tags', function ($q) use ($search) {
$q->where('tag', 'LIKE', "%{$search}%");
});
})
->orderBy($sort, $order)
->paginate($limit)->load("content_type","content_sub_type");
Why isnt the code working ? How can i fix it ?
Model is:
class ContentForSearch extends Model{
use Searchable;
protected $table = 'content';
public function searchableAs()
{
return 'contents_index';
}
public function toSearchableArray()
{
$array = $this->toArray();
// Customize array...
return ["name"=> null];
}
public function content_type()
{
return $this->belongsTo(ContentType::class, 'content_type_id');
}
public function tags()
{
return $this->belongsToMany(ContentTag::class, 'content_content_tags', 'content_id', 'content_tags_id')->withTimestamps();
}
// * * *
The way fulltext search works with algolia and elastic, it seems weird that you want to preserve whereHas() relationship method. In my opinion it is an either or case, either you want sql relation search or you want full text, if you want full text, i would map all the tags at index building instead. So something like this would work.
public function toSearchableArray()
{
$customArray = $this->toArray();
$index = 1;
$this->tags->each(function(Tag $tag) use (&$customArray, &index) {
$customArray['tag' . $index++] = $tag->tag;
});
return $customArray;
}
Which will result in an object similar to this being indexed.
{
'name' => 'x',
'tag1' => 'comment',
'tag2' => 'post',
}
Now searching would provide a similar functionality.
ContentForSearch::search($search);
Goodmorning
I'm trying to make a filter with multiple variables for example I want to filter my products on category (for example 'fruit') and then I want to filter on tag (for example 'sale') so as a result I get all my fruits that are on sale. I managed to write seperate filters in laravel for both category and tag, but if I leave them both active in my productsController they go against eachother. I think I have to write one function with if/else-statement but I don't know where to start. Can somebody help me with this please?
These are my functions in my productsController:
public function productsPerTag($id){
$tags = Tag::all();
$products = Product::with(['category','tag','photo'])->where(['tag_id','category_id'] ,'=', $id)->get();
return view('admin.products.index',compact('products','tags'));
}
public function productsPerCategory($id){
$categories = Category::all(); //om het speciefieke id op te vangen heb ik alle categories nodig
$products = Product::with(['category','tag','photo'])->where('category_id', '=', $id)->get();
return view('admin.products.index',compact('products','categories'));
}
These are my routes in web.php. I guess this will also have to change:
Route::get('admin/products/tag/{id}','AdminProductsController#productsPerTag')->name('admin.productsPerTag');
Route::get('admin/products/category/{id}','AdminProductsController#productsPerCategory')->name('admin.productsPerCategory');
For filter both
change your URL like
Route::get('admin/products/tag/{tag_id?}/{category_id?}','AdminProductsController#productsPerTag')->name('admin.productsPerTag');
Make your function into the controller like
public function productsPerTag($tagId = null, $categoryId = null){
$tags = Tag::all();
$categories = Category::all();
$query = Product::with(['category','tag','photo']);
if ($tagId) {
$query->where(['tag_id'] ,'=', $tagId);
}
if ($tagId) {
$query->where(['category_id'] ,'=', $categoryId);
}
$products = $query->get();
return view('admin.products.index',compact('products','tags', 'categories'));
}
You are trying to filter in your query but you pass only 1 parameter to your controller, which is not working.
1) You need to add your filters as query params in the URL, so your url will look like:
admin/products/tag/1?category_id=2
Query parameters are NOT to be put in the web.php. You use them like above when you use the URL and are optional.
2) Change your controller to accept filters:
public function productsPerTag(Request $request)
{
$categoryId = $request->input('category_id', '');
$tags = Tag::all();
$products = Product::with(['category', 'tag', 'photo'])
->where('tag_id', '=', $request->route()->parameter('id'))
->when((! empty($categoryId)), function (Builder $q) use ($categoryId) {
return $q->where('category_id', '=', $categoryId);
})
->get();
return view('admin.products.index', compact('products', 'tags'));
}
Keep in mind that while {id} is a $request->route()->parameter('id')
the query parameters are handled as $request->input('category_id') to retrieve them in controller.
Hope It will give you all you expected outcome if any modification needed let me know:
public function productList($tag_id = null , $category_id = null){
$tags = Tag::all();
$categories = Category::all();
if($tag_id && $category_id) {
$products = Product::with(['category','tag','photo'])
->where('tag_id' , $tag_id)
->where('category_id' , $category_id)
->get();
} elseif($tag_id && !$category_id) {
$products = Product::with(['category','tag','photo'])
->where('tag_id' , $tag_id)
->get();
} elseif($category_id && !$tag_id) {
$products = Product::with(['category','tag','photo'])
->where('category_id' , $category_id)
->get();
} elseif(!$category_id && !$tag_id) {
$products = Product::with(['category','tag','photo'])
->get();
}
return view('admin.products.index',compact(['products','tags','products']));
}
Route:
Route::get('admin/products/tag/{tag_id?}/{category_id?}','AdminProductsController#productsPerTag')->name('admin.productsPerTag');
I want to get $data['student'] if request get (like: student-guardian?reg_no=0001 in URL).
if no any parameter on url $data['student'] don't needed.
My StudentGuardian function Like:
public function studentGuardian(Request $request)
{
$data = [];
if(!empty($request)){
$data['student'] = Student::select('id','reg_no','reg_date', 'first_name', 'middle_name', 'last_name','faculty', 'semester','status')
->where(function ($query) use ($request) {
if ($request->has('reg_no')) {
$query->where('students.reg_no', 'like', '%'.$request->reg_no.'%');
$this->filter_query['students.reg_no'] = $request->reg_no;
}
})
->get();
}
return view(('studentguardian.index'), compact('data'));
}
This is what you need:
public function studentGuardian(Request $request)
{
$data = [];
// check request has any parameter
if($request->all()){
// query and get list student
$data['student'] = Student::select('id','reg_no','reg_date', 'first_name', 'middle_name', 'last_name','faculty', 'semester','status')
->where(function ($query) use ($request) {
if ($request->has('reg_no')) {
$query->where('students.reg_no', 'like', '%'.$request->reg_no.'%');
$this->filter_query['students.reg_no'] = $request->reg_no;
}
})
->get();
} else {
//do something else
}
// return data to view
return view('studentguardian.index', compact('data'));
}
First, your if(!empty($request)) is useless, $request is always a Request object, not empty.
Second, you can use just if($request->all()) { ... } to check if request array is empty.