Laravel pagination with problems - laravel

i have a simple question as i do not know which line i should add into to get laravel pagination. (->paginate(5).
public function index(Request $request)
{
$codeSearch = $request->get('code');
$descriptionSearch = $request->get('description');
//$tmp = Category::all()->toArray();
$tmp = Category::where('code','like','%' .$codeSearch. '%')->where('description','like','%' .$codeSearch. '%')->get()->toArray();
$category = array();
foreach ($tmp as $key => $row) {
$policy = Category::find($row['parent_id']);
$tmpResult = new Category();
$tmpResult->id = $row['id'];
$tmpResult->code = $row['code'];
$tmpResult->description = $row['description'];
$tmpResult->parent_id = $policy['description'];
$tmpResult->status = $row['status'];
array_push($category, $tmpResult);
}
return view('category.index', compact('category'));
}

Paginate can't be called on the collection, so you have to run it on the query, by simply replace ->get() with ->paginate(5) like this
$tmp = Category::where('code','like','%' .$codeSearch. '%')
->where('description','like','%' .$codeSearch. '%')
->paginate(5)
->toArray();

Try
$tmp = Category::where('code','like','%' .$codeSearch. '%')->where('description','like','%' .$codeSearch. '%')->paginate(5);
In view
#foreach($tmp as $tm)
//whatever operation you like to do
#endforeach
{{$tmp->links()}}

The following ->get(), ->first(), all() get the results from the database and so does ->paginate(5), so I would suggest that you replace ->get() with paginate(5) an you can get rid of toArray() because the result will be a collection with which you can use foreach() or get a value by index.

As you need an array in your condition, then you can simply convert the final array into a collection object and use the pagination concept.
$items = [
'item1',
'item2',
'item3',
'item4',
'item5',
'item6',
'item7',
'item8',
'item9',
'item10'
];
// Get current page form url e.x. &page=1
$currentPage = LengthAwarePaginator::resolveCurrentPage();
// Create a new Laravel collection from the array data
$itemCollection = collect($items);
// Define how many items we want to be visible in each page
$perPage = 1;
// Slice the collection to get the items to display in current page
$currentPageItems = $itemCollection->slice(($currentPage * $perPage) - $perPage, $perPage)->all();
// Create our paginator and pass it to the view
$paginatedItems= new LengthAwarePaginator($currentPageItems , count($itemCollection), $perPage);
// set url path for generted links
$paginatedItems->setPath($request->url());
return view('items_view', ['items' => $paginatedItems]);

It would probably be easier to have a relationship setup for a Category's parent. Then you can load the parent when you retrieve the categories.
class Category extends Model
{
...
public function parent()
{
return $this->belongsTo(self::class);
}
}
I feel like your search is probably trying to do an OR WHERE, search the code field for this value or the description:
$categories = Category::with('parent')
->where('code', 'like', '%' .$codeSearch. '%')
->orWhere('description', 'like', '%' .$codeSearch. '%')
->paginate(5);
Then in your view if you want the parent category's description you can get it through the relationship:
#foreach ($categories as $category)
parent description: {{ $category->parent->description }}
#endforeach

Related

Laravel filter of multiple variables from multiple models

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

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.'%')

Laravel eloquent whereIn with one query

In laravel, I can use many where rules as an array a run one query to the database using laravel eloquent where method to get needed result.
Code:
$where = [];
$where[] = ['user_id', '!=', null];
$where[] = ['updated_at', '>=', date('Y-m-d H:i:s')];
if($request->searchTerm) {
$where[] = ['title', 'like', '%' . $request->searchTerm . '%'];
}
Model::where($where)->get();
Question part:
Now I need to use Laravel Eloquent method whereIn with array params to get needed result with one query.
I tried by looping method but with many queries to the database.
Code:
$model = new Model;
$whereIn = [];
$whereIn[] = ['date', '>=', Carbon::now()->subDays(10)];
$whereIn[] = ['user_role', 'candidate'];
if (!empty($scope) && is_array($scope)) {
$whereIn[] = ['user_id', $scope];
}
if(is_array($employment) && !empty($employment)) {
$whereIn[] = ['employment', $employment];
}
if(is_array($experience) && !empty($experience)) {
$whereIn[] = ['experience', $experience];
}
foreach ($whereIn as $v) {
$model = $model->whereIn($v[0], $v[1]);
}
dump($model->get());
First I tired $model->whereIn($whereIn)->get() but it's return error. It's possible get results with one query using whereIn without looping?
Note: My $whereIn array will be dynamic array!
whereIn is a query builder function so you can't use it on the model directly. Instead you should create a query builder instance. I also suggest you use when instead of the if statements:
$models = Model::when(!empty($scope) && is_array($scope), function ($query) use ($scope) {
$query->whereIn('user_id', $scope);
})->when(!empty($employment) && is_array($employment), function ($query) use ($employment) {
$query->whereIn('employment', $employment);
})->when(!empty($experience) && is_array($experience), function ($query) use ($experience) {
$query->whereIn('experience', $experience);
})->get();
dump($models);
when essentially runs the function when the first parameter is true. There's more detail in the documentation under conditional clauses.
Since your $whereIn variable is an array of arrays it will work like :
$model->whereIn($whereIn[0][0], $whereIn[0][1])->get();
If it just a simple array then you can use :
$model->whereIn($whereIn[0], $whereIn[1])->get();
Do in Eloquent
$model = Model::whereIn('id', array(1, 2, 3))->get();
Or using Query builder then :
$model = DB::table('table')->whereIn('id', array(1, 2, 3))->get();

Laravel 5 Search and pagination url's together

I have a search button and pagination system. They are working perfectly. But when i searched something and page was paginated with numbers. After clicking next page. Search query is gone. After that all values are coming into page2. Not searched values.
My search button changing url like this /en/news?q=foo
My pagination is changing url like this(default btw) /en/news?page=2
How can i add this side by side. Or how can i solve the problem. I am open any solution.
I am using scope for search.
public function scopeSearch($query, $search) {
return $query->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');
if ($query){
$new = $query ? News::search($query)->orderBy('id', 'desc')->paginate(10):News::all();
return view('frontend.news', compact('localeCode', 'updates', 'new', 'query'));
}
else
{
$new = News::orderBy('id', 'desc')->paginate(10);
return view('frontend.news', compact('localeCode', 'updates', 'new', 'query'));
}
}
I hope i can express my own.
{{ $users->appends($_GET)->links() }}
that append all your filter value in one line
It is explained in the documentation for displaying pagination results:
Appending To Pagination Links
You may append to the query string of pagination links using the appends method. For example, to append sort=votes to each pagination link, you should make the following call to appends:
{{ $users->appends(['sort' => 'votes'])->links() }}
I find your current code quite confusing. I think you can rewrite it like this
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'));
}
and in the view you'll have to use
{{ $new->appends(['q' => $query])->links() }}
Try to use {{ $collection->appends(request()->all())->links() }}

Paginate search result laravel

After some help in a previous post Laravel - Search Facility on site using Eloquent I now need to some help on paginating the result using the built in laravel pagination class.
public function search() //no parameter now
{
$q = Input::get('term');
if($q && $q != ''){
$searchTerms = explode(' ', $q);
$query = DB::table('wc_program'); // it's DB::table(), not DB::tables
if(!empty($searchTerms)){
foreach($searchTerms as $term) {
$query->where('JobRef', 'LIKE', '%'. $term .'%');
}
}
$results = $query->get();
dd($results); // for debugging purpose. Use a View here
}
}
Simply change get to paginate and provide number of items per page.
$results = $query->paginate(10);

Resources