Reputation: 3553
in my larave-vue app I want to be able to filter by a search term using laravel scout tntsearch, along with this I also perform additional filtering, sorting or where clauses in my controller. I can't get both to work correctly in my app.
In my controller I have this:
$posts = Post::filter($filters)->search($request->input('query'))->paginate(0);
$posts->load(['postcategory.section','author']);
if($posts->isEmpty())
{
return response()->json([
'message' => 'No results found',
],500);
}
return response()->json([
'message' => 'Results found',
'posts' => $posts,
]);
It gives me the following error:
Call to undefined method Illuminate\Database\Eloquent\Builder::search()
For some reason It's not working, I have tried changing the order of the filter, search and paginate but I still get errors.
In case you are wondering how does my filter method works it's basically a scope
You can read an indepth write up about it here:
https://medium.com/@mykeels/writing-clean-composable-eloquent-filters-edd242c82cc8
See my QueryFilters and Filterable trait bellow just in case
<?php
namespace App\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
class QueryFilters
{
protected $request;
protected $builder;
public function __construct(Request $request)
{
$this->request = $request;
}
public function title($term)
{
$lowerCaseTerm = strtolower($term);
return $this->builder->where('title', 'LIKE', "%$lowerCaseTerm%");
}
public function postCategory($term)
{
return $this->builder->whereHas('postcategory', function ($query) use ($term){
$query->where('id', $term);
});
}
public function sort($term)
{
$sortArray = explode(",", $term);
for($i = 0; $i <= $sortArray.length; $i++)
{
$sortBy = substr_replace($sortArray[i], "", -1);
$sortChar = substr($sortArray[i], -1);
$sortOrder = $sortChar == '+' ? 'ASC' : 'DESC';
$this->bulider->orderBy($sortBy, $sortOrder);
}
return $this->builder;
}
public function apply(Builder $builder)
{
$this->builder = $builder;
foreach ($this->filters() as $name => $value)
{
//if method doesn't exists continue out of the loop
if ( ! method_exists($this, $name))
{
continue;
}
//method exists so check if it has a value payload so call the method with arguments
if (strlen($value))
{
$this->$name($value);
}
//it doesn't have a payload so call the method without arguments
else
{
$this->$name();
}
}
return $this->builder;
}
public function filters()
{
//returns associative array of request body key value pairs
return $this->request->all();
}
}
And Filterable Trait
<?php
namespace App\Filters;
use Illuminate\Database\Eloquent\Builder;
trait Filterable
{
public function scopeFilter($query, QueryFilters $filters)
{
return $filters->apply($query);
}
}
Upvotes: 3
Views: 13274
Reputation: 1989
The search() method is not made to be added to an existing query, here is the method that gets added to your model by the searchable trait
public static function search($query = '', $callback = null)
{
return app(Builder::class, [
'model' => new static,
'query' => $query,
'callback' => $callback,
'softDelete'=> static::usesSoftDelete() && config('scout.soft_delete', false),
]);
}
The function is static, it doesn't take a query builder as a paramater, and returns a new builder no matter what. So you can't chain Search onto a query but you can START with the search and then apply your filters
So instead of this
$posts = Post::filter($filters)->search($request->input('query'))->paginate(0);
Try this
$posts = Post::search($request->input('query'))->filter($filters)->paginate(0);
Upvotes: 3