Simple search in laravel - laravel

I'm trying to build a search system in my small laravel project but considering that I'm still a beginner I can't figure how this should work..
Basically, I have a controller which returns an index page with all the users. In the index page I have multiple select tags where you can choose an option and filter the users.
Here's my index method:
public function index(){
$users = User::all();
$service = Service::all();
return view('browse.index', compact('users','service'));
}
Now, the thing is, I have a relationship built between users and services. So, each user hasMany services. With that in mind, when I'm building the search function I want to be able to access the user service.
Something like this:
$category = $request->input('category');
foreach ($users as $user) {
$serviceFound = $user->service->where('category', 'LIKE', '%' . $category . '%');
}
Of course this doesn't work because considering that I have a get route for my index, I don't know how to set up the form so I can use the $request.. I hope this is clear...Maybe you guys can help me with setting up the form/route and clear my mind on how should I do this...

Build a simple form using GET as well. Simple Example
<form action="" method="GET">
<input type="text" name="category" required/>
<button type="submit">Submit</button>
</form>
In your controller you do
public function index(Request $request){
$category = $request->input('category');
//now get all user and services in one go without looping using eager loading
//In your foreach() loop, if you have 1000 users you will make 1000 queries
$users = User::with('services', function($query) use ($category) {
$query->where('category', 'LIKE', '%' . $category . '%');
})->get();
return view('browse.index', compact('users'));
}
Eager loading will work only if you have your relationships setup properly.
Keep your route unchanged the way it is now.

In your controller:
public function index(\Request $request)
{
return $request::all();
}
In your routes:
Route::get('searchtest', 'SearchController#index');
Example url:
http://localhost:8000/searchtest?search=search_string&category=socks
And finally, the output
{
"search": "search_string",
"category": "socks"
}
You can also do $category = $request::get('category'); to get only the category as a string.

Related

Relationship with paginate

I need to create a dynamic link to take the user to the last page of the laravel page. For example, if there is a pagination, I need to include lastPage () at the end of the url www.site.com/forum/slug?page=2
otherwise it goes to the normal page.
But I need to do this logic on another controller, which in turn is not working. See my relationships and how I'm doing:
public function grupoPerfil($slug) {
$grupo = Grupo::where('slug', $slug)->first();
$comments = Comment::whereHas('relato', function ($query) use ($grupo) {
return $query->where('grupo_id', $grupo->id);
})->paginate(10);
if($grupo){
$rels = $grupo->relatos()->with('user')->paginate(20);
return view('grupo', compact(['grupo', 'rels','comments']));
}
abort(404);
}
my blade:
#foreach($rels->sortByDesc('updated_at') as $r)
<card-relatos
url="{{localized_route('relatoUser', [$r->grupo->slug, $r->slug])}}"
getpaginate="{{ $comments->lastPage() }}"
</card-relatos>
#endforeach
But getpaginate always returns 2, as it is not related to relato_id. How do I solve this?
This is what I have done:
return Product::whereHas('category', function ($query) use ($slug) {
$query->where('slug', $slug);
})->paginate(15);

Do not display duplicated posts on 2nd loop if 1st loop already displays the same posts in laravel

I'm building a dictionary website with laravel6.
And when a visitor search a keyword, 2 queries will run to find posts.
Controller.php
public function index(Request $request)
{
$keyword = $request->input('keyword');
$query = Post::query();
$query2 = Post::query();
if(!empty($keyword)){
$query->where('word','like','%'.$keyword.'%');
$data = $query->orderby('word', 'DESC')->paginate(4);
$query2->where('definition','like','%'.$keyword.'%');
$data2 = $query2->orderby('definition', 'DESC')->paginate(4);
return view('index')->with(['keyword' => $keyword])->with(['data' => $data])->with(['data2' => $data2]);
}
}
Then, show 1st query & 2nd query result.
index.blade.php
#foreach($data as $val)
<div class="post">
<h2>{{$val->word}}</h2>
<p>{{$val->definition}}</p>
</div>
#endforeach
#foreach($data2 as $val2)
<div class="post">
<h2>{{$val2->word}}</h2>
<p>{{$val2->definition}}</p>
</div>
#endforeach
The problem is, 2nd loop ($data2) shows duplicate posts of $data.
So how can I not display duplicate posts on $data2 loop?
I tried like:
#foreach($data2 as $val2)
#if($val2->id !== $val->id)
<div class="post">
<h2>{{$val2->word}}</h2>
<p>{{$val2->definition}}</p>
</div>
#endif
#endforeach
But didn't work. Appreciate any suggestion.
If you use the pagination for queries the only solution is to implement code in the controller. In the second foreach of the view it is not possible to know all the values of the first foreach.
If I understand your question correctly, you can change your approach and use a single query.
In the controller you can write a query more or less like this:
$data = Post::select('word', 'definition')
->where(function ($query) use ($keyword) {
$query->where('word', 'like', '%' . str_replace(' ', '%', $keyword) . '%')
->orWhere('definition', 'like', '%' . str_replace(' ', '%', $keyword);
})
->paginate(4);
You have no duplicate posts and you can also search with multiple words separated by a space (for multiple search the order of words must be that of the text).
EDIT:
This the solution with two query. The first query rest the same, the second query is filtered by the id list of the first query:
$listDataId = Post::where('word','like','%'.$keyword.'%')>orderby('word', 'DESC')->get()->pluck('id');
$query2->where('definition','like','%'.$keyword.'%');
$data2 = $query2->whereNotIn('id', listDataId)->orderby('definition', 'DESC')->paginate(4); // whereNotIn exclude the first query value
You can also do that with laravel's collection. As you might have noticed each query in laravel is a collection, so you can play with collections like you can search here https://laravel.com/docs/7.x/collections#method-merge
public function index(Request $request)
{
$keyword = $request->input('keyword');
$query = Post::query();
$query2 = Post::query();
if(!empty($keyword)){
$query->where('word','like','%'.$keyword.'%');
$data = $query->orderby('word', 'DESC')->paginate(4);
$query2->where('definition','like','%'.$keyword.'%');
$data2 = $query2->orderby('definition', 'DESC')->paginate(4);
//collection starts here.
//It will replace duplicate keys and will give you one collection
$merged = $data->merge($data2);
//Now you will have only one foreach loop in the view.
return view('index')->with(['keyword' => $keyword])->with(['data' => $merged]);
}
}
You can use the following query
$query2->where('definition','like','%'.$keyword.'%')->where('word','not like','%'.$keyword.'%')

How to search in multi column?

I need to search in users table with the name and if there no match with any name in table transfer into category table which has relation with each one of user.
the code of html:
<form action="{{ route('search') }}" method="GET">
<input type="search" class="input" name="q" placeholder="Search">
</form>
Controller code (my shut):
public function search(Request $request)
{
$users = new User;
$req = $request->input('q');
$users = $users->where('name','LIKE','%'. $req .'%')->whereHas('category', function ($query) use($req) {
$query->where('name','LIKE','%'. $req .'%');
});
$users = $users->unverified()->active()->orderBy('id','desc')->paginate(16);
return view('frontend.search.search', compact('users'));
}
category has relation with user:
public function category()
{
return $this->belongsTo('App\Category');
}
I need to say if there is no name ( users table ) check the category one.
You will need to use a orWhere method, which means or do the other. To avoid weird logic it is the easiest approach to wrap it in a where closure.
$users = User::where(function ($query) use($req) {
$query->where('name','LIKE','%'. $req .'%')
->orWhereHas('category', function ($query) use($req) {
$query->where('name','LIKE','%'. $req .'%');
});
});
This means your code will find elements with the name or with the given category. There is an edge case where it will find by the category name even if user is set. But avoiding that, is not as straight forward as this.

Laravel: Passing data to view through route after querying

I recently asked a question about defining many to many relationships (using belongsToMany) and it was a huge help. So now in my models I have:
Users model
public function subjects()
{
return $this->belongsToMany('App\Subject', 'users_subjects', 'user_id', 'subjects_id');
}
Subjects model
public function users()
{
return $this->belongsToMany('App\User', 'users_subjects', 'subject_id', 'user_id');
}
This way I establish a relationship between users and subjects via the users_subjects table. My next step was to create a controller, SubjectsController, which ended up looking like this:
class SubjectsController extends Controller
{
// returns the view where subjects will be displayed
public function index()
{
return view('profiles.professor.prof_didactic_subjects');
}
// get users with subjects
public function getSubjects()
{
$subjects = User::with('subjects')->get();
}
// get a single user with a subject
public function getSubject($id)
{
$materia = User::where('id', '=', $id)->with('subjects')->first();
}
}
I'm not very sure about the code in the controller though.
The final step is where it gets tricky for me, even after reading the docs: I want to pass each result to the view, so I can have multiple tiles, each populated with data from subjects the user is associated with:
#foreach ($subjects as $subject)
<div class="tile is-parent">
<article class="tile is-child box">
<p class="title">{{ $subject['name'] }}</p>
<div class="content">
<p>{{ $subject['description'] }}</p>
</div>
</article>
</div>
#endforeach
I tried many different route configurations, but kept getting either the undefined variable error or trying to access non-object error.
What's the proper course of action here? I feel I'm missing something very basic. Thanks in advance for any help.
The answer
The solution provided below by #Sohel0415 worked perfectly. My index() method on the controller now looks like this:
public function index()
{
// temporary value while I figure out how to get the id of the current user
$user_id = 6;
$subjects = Subject::whereHas('users', function($q) use ($user_id){
$q->where('user_id', $user_id);
})->get();
return view('profiles.professor.prof_didactic_subjects')->with('subjects', $subjects);
}
My route looks like this:
Route::get('/professor', 'SubjectsController#index');
I was pretty lost, so this absolutely saved me, thanks again :)
You need to pass $subjects to your view. You can use compact() method for that like -
public function index()
{
$subjects = Subject::with('users')->get();
return view('profiles.professor.prof_didactic_subjects', compact('subjects'));
}
Or using with() method like -
public function index()
{
$subjects = Subject::with('users')->get();
return view('profiles.professor.prof_didactic_subjects')->with('subjects', $subjects);
}
If you want to get Subject for a particular user_id, use whereHas() -
$subjects = Subject::whereHas('users', function($q) use ($user_id){
$q->where('user_id', $user_id);
})->get();

Laravel 5.4 Relationships Route Binding

I am making an image gallery. Images have a many to many relationship with tags. Images also have a many to many relationship with catagories.
$images = $category->images;
$images = $tag->images;
public function images(Category $category = null)
{
$images = $category->images;
return view('pages.images.index', compact('images'));
}
Route model binding on this code works fine and I can do the same for tags but what I am looking to do is filter on category and then tag.
$images = $tag->category->images;
For example, if I had an image with the tag of "dog" and a category "brown" the query would only return all dogs that are brown.
Can I do this? Both tags and categories could have multiple options so I was hoping for a route like this...
mysite.com/images/{category}/{tag}
Thanks for any help or best direction to go.
You could use the build in laravel whereHas functionality
Image::whereHas('categories', function ($query) use ($category) {
$query->where('NAME', '=', $category);
})->whereHas('tags', function ($query) use ($tag) {
$query->where('NAME', '=', $tag);
})->get();
replace the NAME with the desired field you want to filter on.
You can declare the route just as you stated and then use them as parameters in the controller-method which is connected to the route.
So if you declare a route like Route::get('/register/{token1}/{token2}','RegisterController#confirm');then it is possible to get those wildcards in the method confirm just like normal parameters:
public function confirm(Request $request, $token1, $token2) {}

Resources