laravel-scout-tntsearch-driver icon indicating copy to clipboard operation
laravel-scout-tntsearch-driver copied to clipboard

Using laravel-scout tntsearch along with eloquent query builder

Open GabotronESP opened this issue 4 years ago • 1 comments

Hi everybody, I want the user to sort, filter AND tnt-search Model entries, in my laravel backend I have a search method API call, currently I can't get scout's Model::search method to work along with Eloquent's query builder, so you either:

  1. Search by text if request has 'search'.
  2. Sort or filter with eloquent query builder (see link bellow for more info).

How can I get both the results from tnt-search term search and eloquent query builder to return a merged result, paginated;

My search method:

public function search(Request $request, QueryFilters $filters)
    {

        if($request->has('search'))
        {
            $posts = Post::search($request->input('search'))->paginate(10);
            $posts->load(['postcategory','author','favorites']); 

            if($posts->isEmpty())
            {
                return response()->json([
                    'message' => 'No se encontraron resultados',
                ],500);    
            }

            return response()->json([
                'message' => 'Encontramos unas coincidencias',
                'posts' => $posts,
            ], 200);
        }
        else
        {
            $posts = Post::filter($filters)->with(['postcategory','author','favorites'])->paginate(10);

            if($posts->isEmpty())
            {
                return response()->json([
                    'message' => 'No se encontraron resultados',
                ],500);    
            }

            return response()->json([
                'message' => 'Encontramos unas coincidencias',
                'posts' => $posts,
            ], 200);
        }
        
    }

And in case you are wondering what does Model::filter do, it just iterates through the request query params and uses Eloquent to sort and filter, you can check this medium article:

https://medium.com/@mykeels/writing-clean-composable-eloquent-filters-edd242c82cc8

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

}

GabotronESP avatar Nov 15 '19 20:11 GabotronESP

I went through this same problem. The search method returns a ScoutBuilder object, which is incompatible with the EloquentBuilder object. So I retrieve the search results first, and append those results to my eloquent query:

$keyword = 'something';
$query = Model::query();
$searchQuery = Model::search($keyword);
$query->whereIn('id', $searchQuery->keys()->toArray());

Seems to get the job done.

mindfullsilence avatar Mar 03 '21 02:03 mindfullsilence