I'm trying to make a search feature on laravel. now I'm using this query:
$products = Product::with(['category', 'store'])
->when($keywords, function ($query) use ($keywords) {
$query->where('name', 'LIKE', '%' . $keywords . "%")
->orWhere('description', 'LIKE', '%' . $keywords . '%');
})->get()
The problem is, say on the database i've a product with the name corrupti, If i searched by the exact name or removed a character from beginning or end it works fine, but if I changed a single character it returns an empty list.
Users are likely to make typos, so I want to be able to find the product is the user typed corrupta, corrupi instead of corrupti.
I know this is not a simple task to achive, I googled many things but I didn't find a solution.
One thing I've came accross is the php similar_text funciton, it may be useful but I didn't find a way to include it in the database query.
https://www.php.net/manual/en/function.soundex.php
https://www.php.net/manual/en/function.metaphone.php
use metaphone (or soundex) to encode words you want to be searchable
put them in a database column say products.name_as_metaphone
make your search function encode searched word to metaphone, then make it look in the metaphone column (and not in product.name)...
profit
try this:
$query = Product::query();
$searches = explode(' ', $request->query('q'));
if (isset($searches)) {
foreach ($searches as $search) {
$query->where(function ($q) use ($search) {
$q->where('code', 'ilike', '%' . $search . '%')
->orWhere('name', 'ilike', '%' . $search . '%');
});
}
}
$data = $query->get();
return ([
'success' => true,
'response' => $data,
'message' => 'Your message here'
]);
Related
My Code;
$posts=Term::where('user_id',$user_id)->where('status',1)->where('type','product')->with('preview','attributes','category','price','options','stock','affiliate')->withCount('reviews');
if(!empty($request->term)){
$data= $posts->where('title','LIKE','%'.$request->term.'%');
}
This my code searches for title from term table. But I want to search from another table without breaking the structure. So Example;
if(!empty($request->term)){
$data= $termTABLE->where('title','LIKE','%'.$request->term.'%');
$data= $stockTABLE->where('code','LIKE','%'.$request->term.'%');
}
Since I don't know about Laravel, I couldn't explain it fully. I hope I get help. Thanks.
Good Luck
The below example is not like yours' but you can sort it out according to your need.
For example, if user has a company you can search in the company table like
$user->where('name', 'LIKE', '%' . $searchPhrase . '%') //Searching in user table
//Searching in user table
->orWhere('email', 'LIKE', '%' . $searchPhrase . '%')
//Checking if company exist
->orWhereHas('company', function ($query) use ($searchPhrase) {
//Searching in company table
$query->where('name', 'LIKE', '%' . $searchPhrase . '%');
});
I am trying to search relevant jobs from a list on jobs on my DB according to what user searches for and if the search term matches the job title its highly relevant search result so I am trying to assign the relevancy value to job objects right after their where condition gets satisfied. Also cleaner way of writing orWhere conditions?
public function search()
{
$searchText = $_GET['searchText'];
$jobs = Jobs::where('name', 'LIKE', '%' . $searchText . '%') //how to do $jobs->relevancy = 100 here? if this where condition is satisfied?
->orwhere('skills', 'LIKE', '%' . $searchText . '%')
->orwhere('desc', 'LIKE', '%' . $searchText . '%') //$jobs->relevancy = 10 here
->orwhere('desc1', 'LIKE', '%' . $searchText . '%')
->orwhere('desc2', 'LIKE', '%' . $searchText . '%')
->orwhere('desc3', 'LIKE', '%' . $searchText . '%')
->orwhere('desc4', 'LIKE', '%' . $searchText . '%')
->get();
return view('results', compact('jobs'));
}
If you want to group orWhere conditions to then apply another WHERE (AND) statement, you can use a closure for the grouping.
Imagine that you want something like this:
WHERE A
AND (WHERE B OR WHERE C)
AND (WHERE D OR WHERE E)
Then you could do:
$results = Model::where('A', 'something')
->where(function ($query) {
$query->where('B', 'something')
->orWhere('C', 'something');
})
->where(function ($query) {
$query->where('D', 'something')
->orWhere('E', 'something');
})
->get();
There's no easy way to know which part of the where clauses actually matched the query. You'd need to re-do the text comparisons after you got the results, which is probably not ideal.
In these sort of situations you might want to consider a full text search.
If you're using MySQL you can create a FULLTEXT index and then use MATCH ... AGAINST. (other DBMS have a different syntax but generally there is support).
$searchText = request()->searchText;
$jobs = Jobs::whereRaw('MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE)', [$searchText])
->orWhereRaw('MATCH(desc) AGAINST (? IN NATUAL LANGUAGE MODE)', [$searchText])
// ...
selectRaw('(100*MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE) + 10*MATCH(name) AGAINST (? IN NATUAL LANGUAGE MODE)) as score', [$searchText,searchText])
->addSelect("*")
->orderBy('score', 'desc')
->get();
MATCH ... AGAINST will return a relevance score so by doing e.g. 100*MATCH(name).. you are adding a weight of 100 to that match. The overall result should be ordered based on the total relevance of the search.
More on full text search in MySQL in the manual
If you are using this kind of condition in many places then you can do the following!
1- In your AppServiceProvider inside boot methode
use Illuminate\Database\Query\Builder;
public function boot()
{
Builder::macro('search', function($field, $string){
return $string ? $this->where($field, 'like', '%'.$string.'%') : $this;
});
Builder::macro('orSearch', function($field, $string){
return $string ? $this->orWhere($field, 'like', '%'.$string.'%') : $this;
});
}
2- Do your Query like the following
$jobs = Jobs::where(function($query) {
if($searchText != "") {
$query->search('skills', $searchText);
$query->orSearch('desc', $searchText);
$query->orSearch('desc1', $searchText);
$query->orSearch('desc2',$searchText);
$query->orSearch('desc3',$searchText);
}
})->get();
I think this is a cleaner way of doing this kind of query.
I have this function in my controller:
if ($request->get('query')) {
$query = $request->get('query');
$data = DB::table('users')
->where('is_archive', '=', 0)
->where(function($string) use ($query) {
$string->where('first_name', 'LIKE', '%' . $query . '%')
->orWhere('last_name', 'LIKE', '%' . $query . '%')
->orWhere('church_id', 'LIKE', '%' . $query . '%');
})
->get();
}
What it does is a real-time search result that is listing same exact results as you type a letter. Same as the search bar of google that every time when you type a letter. it will show results that is the same as the currently typed text. I wish to put security features that will prevent the rendering of <, >, ', " etc. but it will accept space (spacebar) character. What I have done so far is:
$sanitizedQuery = strtolower(trim(preg_replace('/\s+/', '', $query)));
but I seem to fail to achieve my desired outcome. Any suggestion is highly appreciated.
I got a problem when search query using Laravel where and like.
$words = 'pa';
$query = Category::where(function ($query) use ($words) {
$query->where('name', 'like', '%'.$words)
->orWhere('name', 'like', $words . '%')
})->pluck('name');
The result is:
[{Chocolate Spa, Zen Spa, Disco Party}]
The expected result is only Party .
I want it search name column starts with the given word not contain that words.
How can I achieve that search method?
This should work:
$query = Category::where('name', 'like', $words.'%')
->orWhere('name', 'like', '% '.$words.'%'))
->pluck('name');
I'm building up a filterable list with Laravel 5.0 and I'm crashing on following Problem.
I get the filter parameters from a HTML form and pass them to the Query Builder, but if a form input stays empty all rows of the table filtered by the other filters should be returned.
Example code:
$collection = Model::withTrashed()
->where('attr1', 'LIKE', \Request::input('attr1', '%'))
->where('attr2', 'LIKE', \Request::input('attr2', '%'))
->where('attr3', 'LIKE', \Request::input('attr3', '%'))
->get();
This seems to be most correct code for me, but it doesn't work as expected. Do you know a good solution for my Problem? I don't want to integrate a messy switch/case statement for proofing for existence and building the collection up manually. :(
Thanks for your help.
It seems that your problem is that you are always passing the filters to the query and you should only pass them if they are not empty.
Maybe somethig like this would work for you:
$results = Model::withTrashed();
if (\Request::input('attr1')) {
$results->where('attr1', 'LIKE', \Request::input('attr1', '%'));
}
if (\Request::input('attr2')) {
$results->where('attr2', 'LIKE', \Request::input('attr2', '%'))
}
if (\Request::input('attr3')) {
$results->where('attr3', 'LIKE', \Request::input('attr3', '%'))
}
$collection = $results->get();
You need to tweak your query a bit like this:
$collection = Model::withTrashed()
->where('attr1', 'LIKE', '%' . \Request::input('attr1') . '%')
->where('attr2', 'LIKE', '%' . \Request::input('attr2') . '%')
->where('attr3', 'LIKE', '%' . \Request::input('attr3') . '%')
->get();
This will make sure that the LIKE clause is still correct when an empty input parameter is sent.