Trait QueryHelpers Documentation
Sopheak\Core\Traits\QueryHelpers is an Eloquent-only utility for building custom ORM-based endpoints. It is not part of the Record CRUD table endpoints and does not affect how /api/v1/{table} works.
Purpose
- Provide a single Eloquent scope (
applyRequestFilters) that converts request query parameters into Eloquent query constraints. - Standardize filtering, sorting, pagination, and eager-loading patterns for custom controllers/services using Eloquent models.
Usage (Clean Architecture)
Model
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Sopheak\Core\Traits\QueryHelpers;
class Invoice extends Model
{
use QueryHelpers;
}Service
namespace App\Services;
use App\Models\Invoice;
use Illuminate\Http\Request;
class InvoiceService
{
public function list(Request $request)
{
return Invoice::query()->applyRequestFilters($request);
}
}Controller
namespace App\Http\Controllers;
use App\Services\InvoiceService;
use Illuminate\Http\Request;
class InvoiceController
{
public function __construct(private readonly InvoiceService $invoices) {}
public function index(Request $request)
{
$result = $this->invoices->list($request);
if ($result instanceof \Illuminate\Database\Eloquent\Builder) {
return response()->json($result->get());
}
return response()->json($result);
}
}Public API
applyRequestFilters (Eloquent scope)
Signature:
public function scopeApplyRequestFilters(
\Illuminate\Database\Eloquent\Builder $builder,
\Illuminate\Http\Request $request,
bool $isArray = false,
string $orderBy = 'id'
)Return behavior:
- Returns
LengthAwarePaginatorwhenper_pageis present. - Returns
LazyCollectionwhenlazy=true. - Returns
array{data: mixed, total: int}whentotal_record=true. - Returns a
Collectionwhen$isArray=true. - Otherwise returns the modified
Eloquent\Builder.
You can also use named arguments when calling the scope (PHP 8+):
$result = Invoice::query()->applyRequestFilters(
request: $request,
isArray: true,
orderBy: 'created_at',
);Supported Query Parameters
Search
s: Searches across the model table's searchable text columns. Example:?s=invoice.search: UsesRecordTableType(searchable: [...])when that table is registered in the record schema. Supports root columns and one-level relationship fields. Example:?search=invoice.
Select (main table only)
select: Selects only columns from the main table (table-prefixed). Example:?select=id,invoice_number,total.
Eager Loading
with: Eloquent eager-loading. Examples:?with=customer,items?with=posts.comments- Column-constrained:
?with=customer(id,name)or?with=customer(*) - JSON array is accepted:
?with=["customer(id,name)","items"]
Sorting
sortby: Sort column (defaults to$orderBy, defaultid)order: Sort direction (ascordesc, defaultdesc)
Result Shape
per_page: Enables pagination.lazy=true: Returns aLazyCollection.limit: Limits results when$isArray=true(max 20000).total_record=true: Returns{ data, total }(useslimitfor the data size).
Join Helpers
join: Table join by name. Example:?join=customerjoinscustomer.id = {main_table}.customer_id.select_join: JSON map of{table: [columns...]}. Example:?select_join={"customers":["name","email"]}.
Filter Operators
This trait supports a smaller, fixed operator set than the dynamic /api/v1/{table} CRUD endpoints (see Standard CRUD Operations for the full list, including negation, date, and Postgres-native operators). Operators are passed the same way, as {column}={operator}.{value}:
is.nulleq.{value},neq.{value}like.{value},contains.{value}gt.{value},gte.{value},lt.{value},lte.{value}in.{a,b,c}between.{start,end},not_between.{start,end}
Multiple columns OR (comma-separated keys):
?invoice_number,reference=contains.ACME
Compare two columns:
?total=compare.gt.balance
Helper Methods (Internal)
These are private methods used by the trait implementation:
applyFilterOperator
private function applyFilterOperator(
\Illuminate\Database\Eloquent\Builder $builder,
string $key,
string $operator,
string $queryValue,
string $tableName
): voidparseSelectColumns
private function parseSelectColumns(string $selectParam, string $tableName): arrayparseWithRelations
private function parseWithRelations(string $withParam): arraycastStringToArray
private function castStringToArray(string $value): arrayhandleOptimizedQuery
private function handleOptimizedQuery(
\Illuminate\Database\Eloquent\Builder $builder,
\Illuminate\Http\Request $request
)