Documentation Index
Fetch the complete documentation index at: https://mintlify.com/charlietyn/rest-generic-class/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The BaseService class handles all business logic for REST operations. It sits between your controller and model, providing:
- CRUD operations with validation
- Dynamic filtering via the
oper parameter
- Eager loading with field selection
- Pagination (standard and cursor-based)
- Hierarchical queries for tree data
- Automatic caching (optional)
- Export helpers for Excel and PDF
Service Setup
Create a service for each model by extending BaseService:
use Ronu\RestGenericClass\Core\Services\BaseService;
use App\Models\Product;
class ProductService extends BaseService
{
public function __construct()
{
parent::__construct(Product::class);
}
}
That’s it! Your service now has all REST capabilities.
Core Methods
list_all()
Retrieves a collection of records with optional filtering, relations, and pagination.
public function list_all($params, $toJson = true): mixed
Query parameters from the request:
select — Column selection
relations — Eager load relations
oper — Filters (and/or conditions)
orderby — Sorting
pagination — Page settings
hierarchy — Hierarchical mode
_nested — Apply relation filters to eager loads
If true, returns ['data' => $results]. If false, returns raw array.
Example:
$params = [
'select' => ['id', 'name', 'price'],
'relations' => ['category:id,name'],
'oper' => ['and' => ['status|=|active', 'price|>=|100']],
'orderby' => [['price' => 'desc']],
'pagination' => ['page' => 1, 'pageSize' => 25]
];
$result = $service->list_all($params);
get_one()
Retrieves a single record matching the query.
public function get_one($params, $toJson = true): mixed
Same parameters as list_all(), but returns the first matching record:
$params = [
'oper' => ['and' => ['sku|=|ABC123']],
'relations' => ['category', 'reviews']
];
$product = $service->get_one($params);
// Returns: ['data' => Product]
create()
Creates one or more records.
public function create(array $params): array
Single record:
$result = $service->create([
'name' => 'Wireless Mouse',
'price' => 29.99,
'stock' => 100,
'category_id' => 5
]);
// Returns: ['success' => true, 'model' => [...]]
Batch creation:
$result = $service->create([
'product' => [
['name' => 'Item 1', 'price' => 10],
['name' => 'Item 2', 'price' => 20]
]
]);
// Returns: ['success' => true, ['model' => ...], ['model' => ...]]
The key name (product) must match your model’s const MODEL value.
update()
Updates an existing record.
public function update(array $attributes, $id, $validate = false): array
Fields to update (only provided fields are changed).
The primary key value or custom field key if fieldKeyUpdate is set.
If true, runs model validation before saving.
Example:
$result = $service->update(
['price' => 24.99, 'stock' => 85],
$productId
);
// Returns: ['success' => true, 'model' => [...]]
Partial updates are supported — only send the fields you want to change:
// Only update stock, leave price unchanged
$service->update(['stock' => 50], $productId);
destroy()
Deletes a record by ID.
public function destroy($id): array
$result = $service->destroy($productId);
// Returns: ['success' => true, 'model' => Product]
Supports soft deletes if your model uses SoftDeletes trait.
Query Building with process_query()
The process_query() method is the heart of the service layer. It transforms REST parameters into an Eloquent query.
From BaseService.php:350-398:
public function process_query($params, $query): Builder
{
// 1. Apply legacy equality filters (attr/eq)
if (isset($params["attr"])) {
$query = $this->eq_attr($query, $params['attr']);
}
// 2. Parse oper parameter
$oper = $params['oper'] ?? null;
if (is_string($oper)) {
$decoded = json_decode($oper, true);
if (json_last_error() === JSON_ERROR_NONE) {
$oper = $decoded;
}
}
// 3. Apply oper tree (includes whereHas for relations)
if (!empty($oper)) {
$query = $this->applyOperTree($query, $oper, 'and', $this->modelClass);
}
// 4. Eager load relations with field selection
if (isset($params['relations'])) {
$query = $this->relations(
$query,
$params['relations'],
$params['_nested'] ? $oper : null
);
}
// 5. Select clause for main model
if (isset($params['select'])) {
$query = $query->select($params['select']);
} else {
$query = $query->select($this->modelClass->getTable() . '.*');
}
// 6. Order by
if (isset($params['orderby'])) {
$query = $this->order_by($query, $params['orderby']);
}
return $query;
}
Order of Execution
- Legacy filters (
attr) — Simple equality conditions
- Dynamic filters (
oper) — Complex filtering tree with whereHas for relations
- Eager loading (
relations) — Loads related data (optionally filtered if _nested=true)
- Column selection (
select) — Limits returned columns
- Sorting (
orderby) — Applies ORDER BY clauses
- Pagination — Applied by
process_all() after process_query()
Filtering System
The service uses HasDynamicFilter trait for powerful query building.
Filter Operators
From HasDynamicFilter.php:35-57:
'=', '!=', '<>', '<', '>', '<=', '>=',
'like', 'not like', 'ilike', 'not ilike',
'in', 'not in',
'between', 'not between',
'date', 'not date',
'null', 'not null',
'exists', 'not exists',
'regexp', 'not regexp'
Conditions use pipe-separated format:
Example filters:
{
"oper": {
"and": [
"status|=|active",
"price|>=|100",
"category_id|in|1,2,3",
"created_at|date|2024-01-01",
"name|like|%wireless%"
]
}
}
See Dynamic Filtering for complete documentation.
Relation Loading
The relations() method handles eager loading with optional field selection.
Basic syntax:
{
"relations": ["category", "reviews"]
}
With field selection:
{
"relations": [
"category:id,name",
"reviews:id,rating,comment"
]
}
Nested relations:
{
"relations": [
"category.parent:id,name",
"reviews.user:id,name"
]
}
See Relation Loading for advanced usage.
Caching
BaseService includes built-in caching for read operations.
Configuration
From config/rest-generic-class.php:
'cache' => [
'enabled' => env('REST_CACHE_ENABLED', false),
'store' => env('REST_CACHE_STORE', 'redis'),
'ttl' => (int)env('REST_CACHE_TTL', 60),
'ttl_by_method' => [
'list_all' => 60,
'get_one' => 30,
],
'cacheable_methods' => ['list_all', 'get_one'],
'vary' => [
'headers' => ['Accept-Language', 'X-Tenant-Id'],
],
]
Cache Keys
Cache keys are deterministic and include:
- Operation name (
list_all, get_one)
- Model class
- Query parameters (
select, oper, relations, etc.)
- User ID (from
auth()->id())
- Vary headers (locale, tenant, etc.)
- Cache version (for invalidation)
From BaseService.php:1127-1151:
private function buildCacheKey(string $operation, array $params): string
{
$request = request();
$route = $request?->route();
$headersToVary = config('rest-generic-class.cache.vary.headers', []);
$varyHeaders = [];
foreach ($headersToVary as $header) {
$varyHeaders[$header] = $request?->header($header);
}
$fingerprint = [
'op' => $operation,
'model' => get_class($this->modelClass),
'route' => $route?->getName() ?? $request?->path() ?? 'cli',
'method' => $request?->method(),
'query' => $request?->query(),
'headers' => $varyHeaders,
'user' => auth()->id(),
'params' => $params,
'version' => $this->getCacheVersion(),
];
return $this->cachePrefix . ':' . sha1(json_encode($fingerprint));
}
Cache Invalidation
The cache version bumps automatically after write operations (create, update, destroy), invalidating all cached queries for that model.
Per-Request Control
{
"cache": false,
"oper": {"and": ["status|=|active"]}
}
Or set custom TTL:
{
"cache_ttl": 300,
"relations": ["category"]
}
Export Helpers
Export features require optional packages:composer require maatwebsite/excel barryvdh/laravel-dompdf
exportExcel()
public function exportExcel($params)
$params = [
'select' => ['id', 'name', 'email', 'created_at'],
'columns' => ['name', 'email'], // Columns to export
'oper' => ['and' => ['active|=|1']],
'filename' => 'active-users.xlsx'
];
return $service->exportExcel($params);
exportPdf()
public function exportPdf($params)
$params = [
'columns' => ['name', 'email', 'created_at'],
'oper' => ['and' => ['active|=|1']],
'template' => 'reports.users', // Blade view
'filename' => 'users-report.pdf'
];
return $service->exportPdf($params);
The Blade template receives:
$data — Array of records
$columns — Column names to display
$model — Model instance
$params — Original parameters
Complete Example
use Ronu\RestGenericClass\Core\Services\BaseService;
use App\Models\Product;
class ProductService extends BaseService
{
public function __construct()
{
parent::__construct(Product::class);
}
// Custom method example
public function getTopRated(int $limit = 10)
{
$params = [
'select' => ['id', 'name', 'price', 'rating'],
'relations' => ['category:id,name'],
'oper' => ['and' => ['rating|>=|4.5']],
'orderby' => [['rating' => 'desc']],
'pagination' => ['page' => 1, 'pageSize' => $limit]
];
return $this->list_all($params);
}
// Override validation
public function create(array $params): array
{
// Custom business logic before creation
if (isset($params['sku'])) {
$params['sku'] = strtoupper($params['sku']);
}
return parent::create($params);
}
}
Next Steps