how can i search multiple keywords in laravel .? - laravel

This is my controller
$term = $request->get('q');
// return $request->q;
$products = Post::whereHas('user', function($query) use($term) {
$query->where('name', 'like', '%'.$term.'%');
})->orWhere('tags','LIKE','%'.$term.'%')->get();
return view('pages.search', compact('products','users'));
This is working good for single keyword search. But I want to use it for multiple keywords. Like laravel php etc. Please guide me when I search multiple values like abc php laravel or anything then it should work. If tags exists in different column like php is present in 1st column and laravel is in next column when I search php laravel then it should show me both column values.

There is probably a better way to do this, like using CloudSearch/Algolia, but this is a solution that has worked for us:
// Split the terms by word.
$terms = explode(" ", request('q'));
$products = Post::query()
->whereHas('user', function ($query) use ($terms) {
foreach ($terms as $term) {
// Loop over the terms and do a search for each.
$query->where('name', 'like', '%' . $term . '%');
}
})
->orWhere(function ($query) use ($terms) {
foreach ($terms as $term) {
$query->where('tags', 'like', '%' . $term . '%');
}
})
->get();

I spent lots of time on this over the past couple days, and here is my solution. It's similar to these other ones but different, so it may be of value for someone one day:
I have a Vue component that makes a GET request, so $request->get('searchTerms') comes from <input name="searchTerms"> in the HTML form submit.
I wanted to allow to user to control which fields were going to be searched, so that's why $search_terms is an array of columns to search.
I wanted it so if the user searched for "McDonalds 604" that it would filter the list of records and show two different matches for that search. One matching "McDonalds" for business_name and one matching "604" for the area code of the phone_number. This would ensure the user would get everything related in case they only knew some fragments.
The goal was to iterate over the search terms and also to iterate over the search fields (columns) with a generic formula that could be applied from any number of search terms and columns, dynamic at runtime.
$search_fields = ['project_name', 'business_name', 'phone_number'];
$search_terms = explode(' ', $request->get('searchTerms'));
$query = Campaign::query();
foreach ($search_terms as $term) {
$query->orWhere(function ($query) use ($search_fields, $term) {
foreach ($search_fields as $field) {
$query->orWhere($field, 'LIKE', '%' . $term . '%');
}
});
}
$filtered = $query->get();
I would recommend reading through the query docs for Laravel because it will give you better intuition about this task: https://laravel.com/docs/5.6/queries
In the above code, we are using parameter grouping and it changes the characteristics of the algorithm if you use where vs. orWhere.
Bonus Factoids
I also highly recommend testing your SQL statements using $query->toSql() and $query->getBindings(). You can put those in place of $query->get() to see what the SQL statement it is generating will look like and what the parameter bindings are. getBindings() will show you what the values of the ? are in toSql().

You need to prepare the $terms array first, then just iterate over it and add orWhere() clauses.
I've just tested it and for whereHas() closure you need to use where() for the first term and orWhere()for the rest of it to make it work:
$posts = Post::whereHas('user', function($q) use($terms) {
$q->where('name', 'like', '%' . $term .'%');
foreach (array_slice($terms, 1) as $term) {
$q->orWhere('name', 'like', '%' . $term .'%');
}
});
foreach ($terms as $term) {
$posts->orWhere('tags', 'like', '%' . $term . '%');
}
$posts = $posts->get();
If you'll decide to use = instead of like, just use whereIn() instead of building query with a bunch of orWhere().

Related

How to make laravel query with search condition and filter condition from relational table

I have models 'Case' and 'Type' in many to many relation. 'Case' belongs to a 'Type'. Now I am trying to implement search and filter in my case index. I have already included the search in the query builder so that the keywords match multiple columns and also columns in related table. Now I am trying to incorportae a filter also. But when I am including that filter in query builder, it is not working. Including either search or filter is working but including both together is not.
Here is my code to include the search in query builder:
$key = $this->search;
$cases = Landcase::query()->orderBy('created_at', 'DESC');
$cases
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
This much is perfectly working. I can search in title, number and type's name. Now here is the additional code to include the filter with type id(s) so that only specific type's cases will be shown and the search will work on those only.
if($this->selected_types){
$ids= $this->selected_types;
$cases->whereHas('type', function ($query) use ($ids){
$query->whereIn('id', $ids);
});
}
this block is not affecting the collection. But if I comment out the first block, this works. How can I incorporate it to my existing query builder block?
You have several OR in your where clauses so adding the last part makes it something like:
WHERE … OR … OR (… AND …)
What you need is to group your orWheres:
$cases->where(function ($query) {
$query
->where('number', 'like', '%'.$this->search.'%')
->orWhere('title', 'like', '%'.$this->search.'%')
->orWhereHas('type', function ($query) use ($key) {
$query->where('name', 'like', $key.'%');
});
});
More here: https://laravel.com/docs/9.x/queries#or-where-clauses

How to find Array values to search in FIND_IN_SET

Hi I am facing a problem with Laravel eloquent query. I have a table named as 'Offer' and it is connected to another table using relation function 'ApplyOn'. here is the code
$offer = Offer::whereHas('ApplyOn',function($query) use ($input){
$query->whereRaw("find_in_set(".$input['size_id'].",size_ids)");
})->get();
here $input['size_id'] is an array so I can't get the result using this query please help me to find out the solution.
if it is not possible with the find_in_set function then how can I solve this problem? I want to sort the data by the given array value.
The $input['size_id'] is an array. for example [1,2,3].
The table field 'size_ids' is a string value with comma-separated. for example 1,2,3,4,5
Offer table
ApplyOn table
You want find all the size Ids matching with the $input['size_id'] and this is an array you can do something like
$offer = Offer::whereHas('ApplyOn', function ($query) use ($input) {
if (isset($input['size_id']) and is_array($input['size_id'])) {
$ids_to_filter = $input['size_id'];
$query->where(function ($sub) use ($ids_to_filter) {
foreach ($ids_to_filter as $value) {
$sub->where('size_ids', 'like', '%' . $value . '%');
}
});
}
})->get();
If with only ONE is a valid ApplOn use an WHERE OR
$sub->orWhere('size_ids', 'like', '%' . $value . '%');
Maybe this can help you
If you just want to filter the data with an array, you can use whereIn.
example -
$offer = Offer::whereHas('ApplyOn', function ($query) use ($input) {
$query->whereIn("field_name", $input['size_id']);
})->get();
If you have saved the data as a string, and need to filter you can use like.
$offer = Offer::whereHas('ApplyOn', function ($query) use ($input) {
$query->where("field_name", 'LIKE', '%' . $input['size_id'] . '%');
})->get();

How to search an item by category and location in laravel simultaneously

The items have category_id and location_id which has relation to tables category and location.
The search form has 3 fields, 1 item keyword, 2nd is the category selection and 3rd is its location.
The user has to fill all the fields in order to be able to search.
The problem is that the search works only for the first input field and brings all the items with the same name but location and categories are not filtered during search...
public function search(Request $request) {
$query = $request->input('query');
$cQuery = $request->input('subCategoryId');
$pQuery = $request->input('province');
$subCategories = Business::where('name', 'like', "%$query%")
->where('province_id', 'like', "%$pQuery%")
->where('sub_category_id', 'like', "%$cQuery%")->get();
return view('pages.search-result')
->with('subCategories', $subCategories);
}
I have assumed that you have the relations setup for all the models. If not, please go through Defining Relationships.
Consider the below answer as a skeleton example for what you are looking for.
$businesses = Business::where('name', 'like', '%' . $query . '%')
->with('province', 'subCategory')
->whereHas('province', function ($q) use ($pQuery) {
$q->where('name', 'like', '%' . $pQuery . '%');
})
->whereHas('subCategory', function ($q) use ($cQuery) {
$q->where('name', 'like', '%' . $cQuery . '%');
})
->get();

Search with multiple tables

I have 3 tables that are connected/have relation.
Posts table have many tag and one Category
Category table have many Posts
Tag table have many Posts
i want a search feature, i know how to search only use Posts (based on title).
I tried to search each tables with Where in my controller but still no luck.
public function Search(Request $request)
{
$search = $request->search;
$posts = post::where('title', 'like', "%{$search}%")->paginate(5);
return view('search', compact('posts'))->with('result', $search);
}
For example
i have a post Titled 'Test' and with Category 'Tost' and with Tags 'Tast and Tust'
so if i type either the title, category or tags i want it to show up. how can i achieve it?
Try use whereHas method (for further info check https://laravel.com/docs/5.8/eloquent-relationships#querying-relations)
So would become something like:
Post::query()
->where('title', 'like', "%$search%")
->orWhereHas('categories', function ($query) use ($search) {
$query->where('name', 'like', "%$search%");
})
You must use like this.
Post::where('title', 'like', '%' . Input::get('search') . '%')->get();

Laravel: searching related data

I have models: Student, Tutor, Country.
Main model is Student with code:
public function studentTutors()
{
return $this->morphedByMany(Tutor::class, 'studentable')
->with('tutorAddresses');
}
Then relations.
Tutor:
public function tutorAddresses()
{
return $this->hasMany(TutorAddress::class, 'tutor_id', 'id')
->with('tutorCountry');
}
TutorAddress:
public function tutorCountry()
{
return $this->hasOne(Country::class, 'country_id', 'country_id')
->where('user_lang', 'en');
}
How do I use it:
$paginator = $student->studentFavouriteTutors()
->getQuery() //for paginate
->where(function ($query) use ($searchPhraze) {
if (strlen(trim($searchPhraze))) {
return $query
->where('username', 'like', '%' . $searchPhraze . '%')
->orWhere('firstname', 'like', '%' . $searchPhraze . '%')
->orWhere('lastname', 'like', '%' . $searchPhraze . '%');
}
})
->paginate($pages, $columns, $pageName, $page);
Question:
I am searching in tutors table (Tutor) for user/first/last names.
Is there are way to search for country name from countries table (Country: tutorCountry)? Lets say, table has 'name' column with country names.
If yes, how should $paginator code look like, to get data from countries table?
Same question goes for relation tutorAddresses. Lets say, table has 'city' column with city names.
Is this possible?
Now, I do not use relations for search, and just do joins.
BTW: I tried hasManyThrough relation, but it does not seem to pass data from that 'through' table, so this is not going to work for me. Also, my 'through' relations go a bit too deep for it (unless I do not understand something as far as this relation is concerned).
EDIT:
Answer by jedrzej.kurylo is perfect!
I just want to add, for all these, who look for a way to search within relation of a relation, like in my case:
studentTutors / tutorAddresses / tutorCountry
... where within model Student, I also want to look for country name inside of Country model, that is deeper in chain of relations and is not directly related to Tutor, but to TutorAddress, which is related to Tutor.
It is just a question of nesting queries:
$searchPhraze = 'France';
$res = $student->studentFavouriteTutors()
//first relation level
->whereHas('tutorAddresses', function($query) use ($searchPhraze) {
//deeper relation
$query->whereHas('tutorCountry', function($query) use ($searchPhraze) {
$query->where('country', 'like', '%' . $searchPhraze . '%');
});
})->get();
Or you can even combine of searches of parent and child relations:
$searchPhraze = 'Hodkiewiczville';
$res = $student->studentFavouriteTutors()
//first relation level
->whereHas('tutorAddresses', function($query) use ($searchPhraze) {
$query
//first level relation search
->where('city', 'like', '%' . $searchPhraze . '%')
//deeper relation
->orWhereHas('tutorCountry', function($query) use ($searchPhraze) {
$query->where('country_label', 'like', '%' . $searchPhraze . '%'));
});
})->get();
Above code found related datasets as expected.
Thou, I am not sure as to benchmark of this.
You can search data in related tables using whereHas() function, e.g.:
$student->studentFavouriteTutors()
->whereHas('tutorAddresses', function($query) use ($searchPhraze) {
$query->where('city', 'like', '%' . $searchPhraze . '%');
})->get();
This will get you all student's favourite tutors that have an address where city column contains given phrase.

Resources