Laravel pagination not working with closure - laravel

I am trying to implement search functionality using Laravel. Here is my method that should return results of search:
$keyword = request('q');
$posts = Post::where('deleted', false)
->where(function($q) use($keyword) {
$q->where('title', 'like', '%' . $keyword . '%')
->orWhere('body', 'like', '%' . $keyword . '%');
})
->latest()
->paginate(5);
The problem is when I search for something that has more than 5 results it shows first 5 results and 2(for example) pages in links, but then when I click on the second page there are somehow 3 pages in links and some of the posts are repeated. I am guessing that closure is causing this problem but I don't know how to fix it.
Any advice is welcome, thanks! :)

Since you're using data from the request, you need to append it to be able to use in the next request:
{{ $posts->appends(['q' => request('q')])->links() }}
From the docs:
You may append to the query string of pagination links using the appends method.

Related

Eloquent - Multiple where Clause and orWhere

I have a little problem to make a query in my laravel application :
I have a table which displays all the customers of my shop.
We are many administrators, and each one have his own shop and his own customers.
The user table has a field 'shop_id' to filter that and the table will only displays the customers who have the same 'shop_id' as the authentified admin.
So I have this query :
$customers = Customer::where('shop_id', Auth::user()->shop_id)->paginate(12);
But we have a lot of customers, so I want to filter those customers. I use a livewire component to make this filter dynamic with a public $search.
So my new query is :
$customers = Customer::where('shop_id', Auth::user()->shop_id)->where('name', 'like', '%' . $this->search . '%')->paginate(12);
Everything's ok if I don't want to add some additionnal filters. But I need to filter by name, by reference_client, and by society name, everything in the same search input.
I thought the next query were correct :
$customers = Customer::where('shop_id', Auth::user()->shop_id)->where('name', 'like', '%' . $this->search . '%')->orWhere('society', 'like', '%' . $this->search . '%')->orWhere('reference', 'like', '%' . $this->search . '%')->paginate(12);
But the orWhere clauses cancel the first where clause (relative to shop_id) : the table displays all the customers, not only the customers who have the good 'shop_id'.
I imagine that I have to combine the 'search' queries in a function, but I have some problem to realize that.
I hope someone could help me, it would be great :)
Thanks in advance
I think I found the solution, a function a the where clause as i thought : $customers = Customer::where('shop_id', Auth::user()->shop_id)->where(function($q) { $query->where('name', 'like', '%'.$this->search.'%')->orWhere('company', 'like', '%'.$this->search.'%')->orWhere('reference', 'like', '%'.$this->search.'%'); })->paginate(12)

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();

How do I query a relation database in Laravel?

I have this search query for Post model in Laravel:
return $q ->where('title', 'like', '%'.$keyword.'%')
Which works totally fine. In my Post model I also have a relation database categories. This is available as an object in my returned post object.
Is there a way I can query the category -> name?
I have tried:
return $q ->where('category->name', 'like', '%'.$keyword.'%')
I believe what you are looking for can be found here: https://laravel.com/docs/5.6/eloquent-relationships#querying-relations
For your use case you need to make sure you have the relationship established in your Post model. If that is set your query should look similar to the following:
return $q->where('title', 'like', '%'.$keyword.'%')->whereHas('category',
function ($query) use ($keyword) {
$query->where('name', 'like', '%'.$keyword.'%');
})->get();
Hope this helps!
If you're db is well structured - then will want to use Laravel's ORM for this stuff.
https://laravel.com/docs/5.6/eloquent#introduction
I assume that you have Models:
Post
Category
Also that you have category_id as a foreign key in Post model.
This is basic structure.
I assume that you want to search Posts and Categories same time so you can do something like this:
return Post::leftJoin('categories', 'posts.category_id', '=', 'categories.id)
->where('categories.name', 'like', '%' . $keyword . '%')
->orWhere('posts.title', 'like', '%' . $keyword . '%')
->get();

Laravel search with localization

I have a scope about searcing. Also using localization from mcamara
Right now, when i am search something, results are coming with different languages. But i can just search with my main language. After results are came translation happening. This is related with my blade.
{!! $news->translate($localeCode)->title !!}
But main question is before the search. How can i search something with selected language. I can switch language easily. This is my search scope.
public function scopeSearch($query, $search) {
$localeCode = LaravelLocalization::getCurrentLocale();
return $query->translate($localeCode)->where('title', 'LIKE', '%' .$search. '%')
->orWhere('sub_body', 'like', '%' .$search. '%')
->orWhere('body', 'like', '%' .$search. '%');
}
Also this is my controller
public function index (Request $request){
$localeCode = LaravelLocalization::getCurrentLocale();
$updates = Update::latest()->take(3)->get();
$query = $request->get('q');
$new = $request->filled('q') ? News::search($query) : News::query();
$new = $new->orderBy('id', 'desc')->paginate(10);
return view('frontend.news', compact('localeCode', 'updates', 'new', 'query'));
}
I'll be glad if anyone help. Thanks advance.

how can i search multiple keywords in 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().

Resources