A solution was to avoid using the paginate function of eloquent but rather to retrieve the SQL result in a Collection, to apply a custom filter and then to use a LengthAwarePaginator with finally a call to toArray to obtain the same result as before:
class InvoiceController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
// Call get to have a Collection
$invoices = Invoice::with(['client'])->filter($request->all())->orderBy('date', 'desc')->get();
// Apply the custom filter
if ($request->has('status') && $request->status == 'paid') {
$invoices = $invoices ->filter(fn($invoice) => $invoice->balance == 0);
}
// Manually apply the paginator
$paginatedInvoices = new LengthAwarePaginator($invoices->forPage(request('page'), 20), $invoices->count(), 20, request('page'), ['path' => URL::current()]);
// Call toArray() function to get (nearly) the same result as with eloquent
return Inertia::render('Invoice/Index', [
'invoices' => $paginatedInvoices->toArray(),
'clients' => Client::getList(),
]);
}
}
Beware of this difference: with eloquent the list in data is an Array while it is an Object with the custom paginator (this is not treated the same way in Vue in the end):
{
"invoices": {
"current_page": 1,
// With paginate of Eloquent : data is an Array
"data": [
{ ... },
{ ... },
],
// With LengthAwarePaginator : data is an Object
"data": {
{ ... },
{ ... },
},
"first_page_url": "http://localhost:8000/invoices/index?page=1",
"from": 1,
"last_page": 3,
"last_page_url": "http://localhost:8000/invoices/index?page=3",
"links": [ ... ],
"next_page_url": "http://localhost:8000/invoices/index?page=2",
"path": "http://localhost:8000/invoices/index",
"per_page": 20,
"prev_page_url": null,
"to": 20,
"total": 56
}
}