Search all method missing where argument in Laravel 5 - php

I'm trying to write a method in my controller to allow searching the results of my query on my view page. The query selects all results from the "ads" table and this method should allow filtering results by the ad's name inputting keywords in the search bar.
The controller code goes like this:
public function index(Request $request)
{
$title = trans('ad.title');
$ads = Ad::paginate(10);
if (!empty($request->input('search_all'))) {
$search_all = urldecode($request->input('search_all'));
$ads->where(function ($query) use ($search_all) {
$query->where('name', 'like', '%'.$search_all.'%')->get();
});
}else {
// Returning to view
return view('admin.ad.list')
->with('ads', $ads)
->with('title', $title);
}
}
However, when I run a search I get the following error: "Missing argument 2 for Illuminate\Support\Collection::where()".
What am I doing wrong?

At the top, when doing $ads = Ad::paginate(10); you have already pulled the results from the database so no more filtering can be done at DB level.
I'd do something like this instead:
public function index(Request $request)
{
$title = trans('ad.title');
$ads = Ad::select();
if ($request->input('search_all')) {
$search_all = urldecode($request->input('search_all'));
$ads->where('name', 'like', '%'.$search_all.'%');
}
// Returning to view
return view('admin.ad.list')
->with('ads', $ads->paginate(10))
->with('title', $title);
}

Related

Search in data from cache

My question is, if there is data in the cache, I created a condition by saying fetch, and if not, I bring it from the database and refresh the cache for a certain period of time. Normally, if I don't use the cache, I can search the page, but I cannot search by id or nickname with the code below.So when using cache::get can I add a search condition?
public function index(Request $request)
{
$submit = $request->get('submit');
$id = $request->get('id');
$nick_name = $request->get('nick_name');
if (Cache::has('users')) {
$users = Cache::get('users');
return view('admin.users.index', compact('users'));
}
$users = User::when(!empty($id), function ($query) use ($id) {
$query->where('id', $id);
})->when(!empty($nick_name), function ($query) use ($nick_name) {
$query->where('nick_name', $nick_name);
})->orderBy('created_at', 'asc')->get();
Cache::put('users', $users, now()->addMinutes(60));
return view('admin.users.index', compact('users'));
}
Update: Based on the examples on the page below, the problem was solved
https://www.honeybadger.io/blog/caching-in-laravel/
You should add search criteria in your cache key. E.g.
public function index(Request $request) {
$submit = $request->get('submit');
$id = $request->get('id');
$nick_name = $request->get('nick_name');
$cacheKey = User::class . md5(json_encode([$submit, $id, $nick_name]));
if (Cache::has($cacheKey)) {
/* ... */
}
because now your cache writes only first search result and ignores everything else.
Or write to cache all users. Then run that array through array_filter applying filters. But that can be waste of developer time if search is very dynamic.

Laravel filters won't work when trying to add them

So I'm making a fliter in laravel for a project and me and my teacher are braking our head on a the following thing.
In the code below the general search for a player works but the other statements won't add to it if they are defined and in the POST request;
Controller:
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Input;
class FilterController extends Controller
{
public function filter(Request $request)
{
$player = new \App\Player;
$filters = $player->newQuery();
$query = Input::get('q');
// Search for a player based on their status.
if ($request->has('status')) {
$filters->orwhere('status', $request->input('status'));
}
// Search for a player player on their club.
if ($request->has('club')) {
$filters->orwhere('Club', $request->input('club'));
}
// Search for a player player on their team category .
if ($request->has('Category')) {
$filters->orwhere('Category', $request->input('Category'));
}
// Search for a player player if he is flagged as removed.
if ($request->has('remove')) {
$filters->orwhere('remove', $request->input('remove'));
}
// Search for a player player on their size.
if ($request->has('size')) {
$filters->orwhere('Size', $request->input('size'));
}
// General search for a player
if($request->has('q')){
$filters->orwhere('first_name','LIKE','%'.$query.'%')
->orWhere('last_name','LIKE','%'.$query.'%')
->orWhere('mobile','LIKE','%'.$query.'%')
->orWhere('street_name_nummer','LIKE','%'.$query.'%')
->orWhere('city','LIKE','%'.$query.'%');
}
// Get the results and return them.
$results = $filters->get();
if(count($results) > 0){
return view('lists/ekick')->withDetails($results,$query);
} else return view ('lists/ekick')->with('No Details found. Try to search again !');
}
}
route:
Route::any('lists/ekick', 'FilterController#filter');
output view:
img from view
To pick up on Aaron Sarays answer, you are most likely filtering the wrong way. Normally additional filters are additional conditions each record has to meet in order to be part of the result. If you consider an Excel table and you filter one column, you won't even have all options to filter for in the second column because you already limited the result and you can only limit it further.
Combine this knowledge with an improved way of filtering and you receive a query like this:
public function filter(Request $request)
{
$query = $request->input('q');
$results = \App\Player::query()
->when($request->input('status'), function ($query, $status) {
$query->where('status', $status);
})
->when($request->input('club'), function ($query, $club) {
$query->where('club', $club);
})
->when($request->input('category'), function ($query, $category) {
$query->where('category', $category);
})
->when($request->input('remove'), function ($query, $remove) {
$query->where('remove', $remove);
})
->when($request->input('size'), function ($query, $size) {
$query->where('size', $size);
})
->when($query, function ($query, $q) {
$query->where(function ($query) use ($q) {
$query->where('first_name', 'LIKE', "%$q%")
->orWhere('last_name', 'LIKE', "%$q%")
->orWhere('mobile', 'LIKE', "%$q%")
->orWhere('street_name_number', 'LIKE', "%$q%")
->orWhere('city', 'LIKE', "%$q%");
});
})
->get();
if ($results->isNotEmpty()) {
return view('lists/ekick')->withDetails($results, $query);
} else {
return view ('lists/ekick')->with('No Details found. Try to search again !');
}
}
The function when($condition, $callback) as used in the query above is used to dynamically build queries. You can consider the following two statements equivalent:
// option 1: conditional query (preferred!)
$results = Player::query()
->when($request->input('q'), function ($query, $q) {
$query->where('name', 'LIKE', "%$q%");
})
->get();
// option 2: plain php query building... (not very clean code)
$query = Player::query();
if ($request->input('q')) {
$query->where('name', 'LIKE', '%'.$request->input('q').'%');
}
$results = $query->get();
In order to do what you're doing, I think you want to not use or with your queries. You're basically saying
Give me the Player where status is something or size is something
I think what you mean to say is
Give me the Player where status is something and size is something
Depending on if the requirements exist or not in the filter.
So, you'd want to alter your code using the following as an example:
if ($request->has('status')) {
$filters->where('status', $request->input('status'));
}
// Search for a player player on their club.
if ($request->has('club')) {
$filters->where('Club', $request->input('club'));
}
You can also bypass one step by using this:
$query = \App\Player::getQuery();

Call to undefined method appends()

I created a filter for db table, but filtration just work for first page
I try this code in controller
public function index(Request $request)
{
$user=User::orderBy('created_at', 'ASC');
if($request->get('search'))
{
if ($request->get('type')) {
$user->where('type', $request->get('type'));
}
if ($request->get('id')) {
$user->where('id', (integer)$request->get('id'));
}
if ($request->get('first_name')) {
$user->where('first_name', $request->get('first_name'));
}
if ($request->get('email')) {
$user->where('email', $request->get('email'));
}
}
$users = $user->paginate(20);
$user->appends(['search' => $request->get('search')]);
return view('admin.user.index', compact('users'));
}
But I get this error
Call to undefined method Illuminate\Database\Query\Builder::appends()
How I can fix this error or how I can make filter works with all pages ?
As I can see you have used following line and paginate the user data in users
$users = $user->paginate(20);
So you need to use users variable instead of user
$users->appends(['search' => $request->get('search')]);

Nested Query in Laravel 5.2

I Have a search box that I want to use to search some of columns of a table in the database. Here's the code
$project = Project::findOrFail($id);
$file = \App\File::find($file);
$query = $request->input('q');
$materials = $query
?\App\Material::where('file_id', '=', $file->id)
->where('material_name', 'LIKE', "%$query%" )->get()
:\App\Material::where('file_id', '=', $file->id)->get();
return view('projects.file',compact('project', 'file', 'materials'));
The data as it is when the page loads is filtered to show just the items from this project. But when the search is done, it searches the whole table. How can I make it search only the items from the specific project and not the whole items from the table?
You can nest your search items this way:
public function handle($request, Closure $next)
{
$project = Project::findOrFail($id);
$file = \App\File::find($file);
$materials = \App\Material::newQuery();
// Did it found the file?
if ($file) {
// Search for the file
$materials->where('file_id', '=', $file->id);
}
// AND, does it have a query to search?
if ($search_item = $request->input('q')) {
// Search for the query
$materials->where('material_name', 'LIKE', "%$search_item%");
}
// AND, does it have a project to filter?
if ($search_item = $request->input('project')) {
// Filter the project
$materials->where('project', 'LIKE', "%$search_item%");
}
// Now go get the results
$materials = $materials->get();
// And return to the view
return view('projects.file',compact('project', 'file', 'materials'));
}

Laravel previous and next records

I am trying to create a page where I can see all the people in my database and create edits on them. I made a form where I fill in the data from the database of certain fields.
I would like to navigate trough them by a Next and Previous button.
For generating the next step I have to take the ID larger than the current one to load the next profile.
For generating the previous step I have to take the ID smaller than the current one to load the previous profile.
My route:
Route::get('users/{id}','UserController#show');
Controller:
public function show($id)
{
$input = User::find($id);
// If a user clicks next this one should be executed.
$input = User::where('id', '>', $id)->firstOrFail();
echo '<pre>';
dd($input);
echo '</pre>';
return View::make('hello')->with('input', $input);
}
View:
The buttons:
Next
What is the best approach to get the current ID and increment it?
Below are your updated controller and view files derived from #ridecar2 link,
Controller:
public function show($id)
{
// get the current user
$user = User::find($id);
// get previous user id
$previous = User::where('id', '<', $user->id)->max('id');
// get next user id
$next = User::where('id', '>', $user->id)->min('id');
return View::make('users.show')->with('previous', $previous)->with('next', $next);
}
View:
Previous
Next
// in your model file
public function next(){
// get next user
return User::where('id', '>', $this->id)->orderBy('id','asc')->first();
}
public function previous(){
// get previous user
return User::where('id', '<', $this->id)->orderBy('id','desc')->first();
}
// in your controller file
$user = User::find(5);
// a clean object that can be used anywhere
$user->next();
$user->previous();
In your App\Models\User.php
...
protected $appends = ['next', 'previous'];
public function getNextAttribute()
{
return $this->where('id', '>', $this->id)->orderBy('id','asc')->first();
}
public function getPreviousAttribute()
{
return $this->where('id', '<', $this->id)->orderBy('id','asc')->first();
}
In your Controller you can simply do this:
public function show(User $user)
{
return View::make('users.show')
->with('user', $user)
->with('previous', $user->previous)
->with('next', $user->next);
}
I understand the approach being taken here by user2581096 but I am not sure it is efficient (by any standards). We are calling the database 3 times for really no good reason. I suggest an alternative that will be way more efficient and scalable.
Do not pass the previous and next IDs to the view. This eliminates 2 unnecessary database calls.
Create the following routes:
users/{id}/next
users/{id}/previous
These routes should be used in the href attributes of the anchor tags
Add methods in the controller to handle each of the new routes you have created. For example:
public function getPrevious(){
// get previous user
$user = User::where('id', '<', $this->id)->orderBy('id','desc')->first();
return $this->show($user->id);
}
This function will only be called when you actually click on the button. Therefore, the database call is only made when you need to actually look up the user.
in-case you want to retrieve the prev/next records along with their data,
you can try
$id = 7; // for example
$prev = DB::table('posts')->where('id', '<', $id)->orderBy('id','desc')->limit(1);
$next = DB::table('posts')->where('id', '>', $id)->limit(1);
$res = DB::table('posts')
->where('id', '=', $id)
->unionAll($prev)
->unionAll($next)
->get();
// now $res is an array of 3 objects
// main, prev, next
dd($res);
1- the query builder is usually much faster than eloquent.
2- with union we are now only hitting the db once instead of 3.
To get next and previous post we can use max and min functions on Model id in laravel. here is an example to get this
https://usingphp.com/post/get-next-and-previous-post-link-in-laravel
The Controller:
public function post($id)
{
$post = Post::find($id);
$previous = Post::where('id', '<', $post->id)->max('id');
$next = Post::where('id', '>', $post->id)->min('id');
return view( 'post', compact( 'post', 'next', 'previous' ));
}
The View:
#if($next)
{{$next->title}}
#endif
#if($previous)
{{$previous->title}}
#endif
Here's a link I found that should help: http://maxoffsky.com/code-blog/laravel-quick-tip-get-previous-next-records/
It looks like for next you want to use: $next = User::where('id', '>', $id)->min('id'); and have the view as: Next
Also don't forget to pass $next to the view.
Simplest approach
// User.php
public static function findNext($id)
{
return static::where('id', '>', $id)->first();
}
// UserController.php
$nextUser = User::findNext($id);
// view
Next
Lazy approach :
// view
Next
// routes.php (should be optimized, this is just to show the idea)
Route::get('users/{user}/next', function($id) {
$nextUser = User::findNext($id);
return Redirect::to('user/' . $id);
});
// yourModel.php
public function previous()
{
return $this->find(--$this->id);
}
public function next()
{
return $this->find(++$this->id);
}
Works like magic, you can chain it:
$prevprev = Model::find($id)->previous()->previous();
$nextnext = Model::find($id)->next()->next();
First, get a record out of the database.
$post = Post::where('slug', $slug)->first();
With a database record, we can get the previous record where the record id is less than the id stored inside $post order by the id in descending order and use first() to get a single record back.
$previous = Post::where('id', '<', $post->id)->orderBy('id','desc')->first();
To get the next record it's almost the same query, this time get the record where the id is more than the id stored in $post.
$next = Post::where('id', '>', $post->id)->orderBy('id')->first();
Controller:
public function show($id)
{
// get the current user
$user = User::find($id);
// get previous user id
$previous = User::offset($user->id-2)->first();
// get next user id
$next = User::offset($user->id)->first();
return View::make('users.show')->with('previous', $previous)->with('next', $next);
}
i developed the code.
it work all times, even if we don't have any next or prev post
public function nextPost($table, $id)
{
$next = DB::table($table)->where('id', '>', $id)->orderBy('id','asc')->first();
if(!$next)
$next = DB::table($table)->orderBy('id','asc')->first();
return $next;
}
public function prevPost($table, $id)
{
$prev = DB::table($table)->where('id', '<', $id)->orderBy('id','desc')->first();
if(!$prev)
$prev = DB::table($table)->orderBy('id','desc')->first();
return $prev;
}

Categories