Closed francoism90 closed 3 years ago
Yep! That same approach would still work!
@Tucker-Eric Thanks! That indeed seems to work.
However I'm thinking of this approach with Meilisearch:
public function handle()
{
parent::handle();
$filters = $this->query->get()->map(function($model) {
return 'id = '.$model->id;
})->join(' OR ');
// Overwrite QueryBuilder with ScoutBuilder
$this->query = $this->getModel()->search(
$this->input('query', ''),
function ($engine, string $query, array $options) use ($filters) {
$options['filters'] = $filters;
return $engine->search($query, $options);
}
);
return $this->query;
}
This would mean I can use the filters and return a Laravel Scout (pagina) instance afterwards.
Any downsides of using this approach?
Thanks!
That looks like it works. I don't see any downsides. Maybe some minor optimizations and use a whereIn
instead of joining the results on OR
@Tucker-Eric Unfortunately it doesn't seem work with a large dataset.
The query simple selects all results (e.g. select * from books
in handle
) each time doing a request. The final query uses take/skip, which actually is correct because of adding the paginate method in the controller.
The only workaround I think would be to integrate the paginate method in handle (ModelFilter class) instead of adding it afterwards?
Ps. I need to use the direct filter with OR unfortunately, this is a limitation of the engine as it doesn't provide any whereIn.
Are you referring to something like this?
public function handle()
{
$filters = parent::handle()
->paginateFilter()
->getCollection()
->pluck('id')
->join(' OR ')
// Overwrite QueryBuilder with ScoutBuilder
return $this->query = $this->getModel()->search(
$this->input('query', ''),
function ($engine, string $query, array $options) use ($filters) {
$options['filters'] = $filters;
return $engine->search($query, $options);
}
);
}
@Tucker-Eric Thanks for helping me., really appreciate it :)
$filters = parent::handle()
->paginateFilter()
->getCollection()
->pluck('id')
->map(fn($id) => 'id = '.$id)
->join(' OR ');
// Overwrite QueryBuilder with ScoutBuilder
return $this->query = $this->getModel()->search(
$this->input('query', ''),
function ($engine, string $query, array $options) use ($filters) {
$options['filters'] = $filters;
return $engine->search($query, $options);
}
);
This works fine. :)
The first page is loaded correctly, trying to load the second page seems to reset the paginate collection.
This is my IndexController.php
:
class IndexController extends Controller
{
public function __invoke(Request $request): ResourceCollection
{
$books = Book::filter($request->all(), BookFilter::class)->simplePaginate();
return BookResource::collection($books);
}
}
Should I call something different here?
I would almost suggest using scout search to get the id's instead of the filter and then filter and paginate in the filter. You could even throw that into a query
method that will automatically get resolved when that input parameter is present.
public function query($query)
{
$ids = $this->getModel()
->search($query)
->get()
->pluck('id');
return $this->whereIn('id', $ids);
}
Then your controller can work the same or you can use the simplePaginateFilter
in the controller:
class IndexController extends Controller
{
public function __invoke(Request $request): ResourceCollection
{
$books = Book::filter($request->all(), BookFilter::class)->simplePaginateFilter();
return BookResource::collection($books);
}
}
@Tucker-Eric This seems indeed the best solution. Maybe Laravel Scout is too limited and should add some features to make it easier to integrate with the QueryBuilder.
Thanks for the help and your time. :)
I'm exploring the best way to integrate Laravel Scout using (custom) filters and it seems #37 ([https://github.com/Tucker-Eric/EloquentFilter/issues/37#issuecomment-321892146]) provides some information how one can integrate this nicely.
However that ticket is created a few years ago and I don't know if things should be done differently nowadays.
Could you please tell me if the same solution still is valid?
Many thanks!