Overview
The RestController class provides a complete REST API interface with minimal configuration. It handles:
- Request parameter processing (select, relations, oper, pagination, etc.)
- Standard CRUD endpoints (index, show, store, update, destroy)
- Validation integration via FormRequest classes
- Database error handling with user-friendly messages
- Transaction management for write operations
- Export endpoints for Excel and PDF
Controller Setup
Create a controller by extending RestController:
use Ronu\RestGenericClass\Core\Controllers\RestController;
use App\Services\ProductService;
use App\Models\Product;
class ProductController extends RestController
{
protected BaseModel|string $modelClass = Product::class;
protected BaseService|string $service;
public function __construct(ProductService $service)
{
$this->service = $service;
}
}
That’s it! Your controller now has all REST endpoints.
Route Registration
Register the controller in your routes file:
use App\Http\Controllers\ProductController;
Route::prefix('api/v1')->group(function () {
Route::get('/products', [ProductController::class, 'index']);
Route::get('/products/get-one', [ProductController::class, 'getOne']);
Route::post('/products', [ProductController::class, 'store']);
Route::get('/products/{id}', [ProductController::class, 'show']);
Route::put('/products/{id}', [ProductController::class, 'update']);
Route::delete('/products/{id}', [ProductController::class, 'destroy']);
// Optional: bulk operations
Route::put('/products/bulk', [ProductController::class, 'updateMultiple']);
Route::delete('/products/bulk', [ProductController::class, 'deleteById']);
// Optional: exports
Route::get('/products/export/excel', [ProductController::class, 'export_excel']);
Route::get('/products/export/pdf', [ProductController::class, 'export_pdf']);
});
Or use resource routing:
Route::apiResource('products', ProductController::class);
Available Endpoints
index() — List Records
Retrieves a paginated list of records with filtering, relations, and sorting.
public function index(Request $request): LengthAwarePaginator|array|CursorPaginator
Request example:
GET /api/v1/products?select=["id","name","price"]&relations=["category:id,name"]&oper={"and":["status|=|active"]}&pagination={"page":1,"pageSize":25}
Response:
{
"data": [
{
"id": 1,
"name": "Wireless Mouse",
"price": 29.99,
"category": {
"id": 5,
"name": "Electronics"
}
}
]
}
Or with pagination:
{
"current_page": 1,
"data": [...],
"per_page": 25,
"total": 150
}
getOne() — Get Single Record by Query
Retrieves the first record matching the query parameters.
public function getOne(Request $request): array
Request example:
GET /api/v1/products/get-one?oper={"and":["sku|=|ABC123"]}&relations=["category"]
Response:
{
"data": {
"id": 1,
"name": "Wireless Mouse",
"sku": "ABC123",
"category": {...}
}
}
show() — Get Record by ID
Retrieves a single record by its primary key.
public function show(Request $request, $id): mixed
Request example:
GET /api/v1/products/123?relations=["category","reviews"]&select=["id","name","price"]
Response:
{
"id": 123,
"name": "Wireless Mouse",
"price": 29.99,
"category": {...},
"reviews": [...]
}
show() supports the same relations, select, and hierarchy parameters as index().
store() — Create Record
Creates a new record with validation.
public function store(BaseFormRequest $request): array
Request example:
POST /api/v1/products
Content-Type: application/json
{
"name": "Wireless Mouse",
"price": 29.99,
"stock": 100,
"category_id": 5
}
Response:
{
"success": true,
"model": {
"id": 124,
"name": "Wireless Mouse",
"price": 29.99,
"stock": 100,
"category_id": 5,
"created_at": "2024-03-15T10:30:00Z",
"updated_at": "2024-03-15T10:30:00Z"
}
}
Batch creation:
{
"product": [
{"name": "Item 1", "price": 10},
{"name": "Item 2", "price": 20}
]
}
The key name (product) must match your model’s const MODEL value.
update() — Update Record
Updates an existing record by ID.
public function update(Request $request, $id): mixed
Request example:
PUT /api/v1/products/123
Content-Type: application/json
{
"price": 24.99,
"stock": 85
}
Response:
{
"success": true,
"model": {
"id": 123,
"name": "Wireless Mouse",
"price": 24.99,
"stock": 85,
"updated_at": "2024-03-15T11:00:00Z"
}
}
Partial updates are supported — only send the fields you want to change.
destroy() — Delete Record
Deletes a record by ID.
public function destroy($id): array
Request example:
DELETE /api/v1/products/123
Response:
{
"success": true,
"model": {
"id": 123,
"name": "Wireless Mouse",
"deleted_at": "2024-03-15T11:30:00Z"
}
}
Request Processing
The process_request() method extracts and normalizes query parameters.
From RestController.php:79-102:
public function process_request(Request $request): array
{
$parameters = [];
$payloads = array_merge($request->query(), $request->request->all());
// Extract all supported parameters
$parameters['relations'] = $request['relations'] ?? null;
$parameters['_nested'] = $request['_nested'] ?? false;
$parameters['soft_delete'] = $request['soft_delete'] ?? null;
// Merge attr and eq parameters (legacy support)
if (array_key_exists('attr', $payloads) && array_key_exists('eq', $payloads)) {
$parameters['attr'] = array_merge(
(array)$request['attr'],
(array)$request['eq']
);
} elseif (array_key_exists('attr', $payloads)) {
$parameters['attr'] = $request['attr'];
} elseif (array_key_exists('eq', $payloads)) {
$parameters['attr'] = $request['eq'];
} else {
$parameters['attr'] = null;
}
$parameters['select'] = $request['select'] ?? "*";
$parameters['pagination'] = $request['pagination'] ?? null;
$parameters['orderby'] = $request['orderby'] ?? null;
$parameters['oper'] = $request['oper'] ?? null;
$parameters['hierarchy'] = $request['hierarchy'] ?? null;
return $parameters;
}
Supported Parameters
Columns to return. Example: ["id","name","price"] or "*"
Relations to eager load. Example: ["category:id,name","reviews"]
Dynamic filters. Example: {"and":["status|=|active"]}
Sorting rules. Example: [{"price":"desc"},{"name":"asc"}]
Page settings. Example: {"page":1,"pageSize":25}
Hierarchical mode. Example: true or {"filter_mode":"with_descendants","max_depth":3}
Apply relation filters to eager loads.
Legacy equality filters. Example: {"status":"active","category_id":5}
Error Handling
RestController includes automatic database error handling via DatabaseErrorParser.
From RestController.php:58-72:
protected function handleDatabaseException(\Throwable $e): DatabaseErrorParserException
{
// Parse the error
$parsedError = DatabaseErrorParser::parse($e);
// Log the full error for debugging
Log::channel('rest-generic-class')->error('Database error occurred', [
'parsed' => $parsedError,
'exception' => $e,
'trace' => $e->getTraceAsString(),
]);
// Return user-friendly exception
return DatabaseErrorParser::toExceptionError($parsedError);
}
Error Types
The parser detects and translates common database errors:
- Foreign key constraint violations → “Cannot delete: record is referenced by other records”
- Unique constraint violations → “Duplicate value for field ‘email’”
- Not null violations → “Field ‘name’ is required”
- Connection errors → “Database connection failed”
Example error response:
{
"message": "Foreign key constraint violation",
"details": "Cannot delete product: referenced by 5 orders",
"constraint": "orders_product_id_foreign",
"table": "products"
}
Transaction Management
Write operations automatically use database transactions.
From RestController.php:158-176 (store method):
public function store(BaseFormRequest $request): array
{
DB::beginTransaction();
try {
$params = count($request->all()) != 0
? $request->all()
: json_decode($request->getContent(), true) ?? [];
$result = $this->service->create($params);
if ($result['success'])
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
Log::channel('rest-generic-class')->error('Store failed', [
'controller' => static::class,
'method' => __FUNCTION__,
'exception' => $e,
]);
throw $e;
}
return $result;
}
All write operations (store, update, updateMultiple, destroy) follow this pattern:
- Begin transaction
- Execute operation
- Commit if successful
- Rollback on error
- Log failures
Validation Integration
Use BaseFormRequest for custom validation:
use Ronu\RestGenericClass\Core\Requests\BaseFormRequest;
class StoreProductRequest extends BaseFormRequest
{
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0',
'category_id' => 'required|exists:categories,id',
'status' => 'in:draft,active,archived',
];
}
}
Update your controller:
use App\Http\Requests\StoreProductRequest;
class ProductController extends RestController
{
public function store(StoreProductRequest $request): array
{
return parent::store($request);
}
}
Now validation runs automatically before store() executes.
Bulk Operations
updateMultiple()
Update multiple records in one request.
public function updateMultiple(Request $request): mixed
Request example:
PUT /api/v1/products/bulk
Content-Type: application/json
{
"product": [
{"id": 1, "stock": 100},
{"id": 2, "stock": 50},
{"id": 3, "stock": 75}
]
}
Response:
{
"success": true,
"models": [
{"success": true, "model": {...}},
{"success": true, "model": {...}},
{"success": true, "model": {...}}
]
}
deleteById()
Delete multiple records by IDs.
public function deleteById(Request $request): array
Request example:
DELETE /api/v1/products/bulk
Content-Type: application/json
[1, 2, 3]
Export Endpoints
export_excel()
GET /api/v1/products/export/excel?columns=["name","price","stock"]&oper={"and":["status|=|active"]}&filename=products.xlsx
Returns an Excel file download.
export_pdf()
GET /api/v1/products/export/pdf?columns=["name","price"]&template=reports.products&filename=products.pdf
Returns a PDF file download using the specified Blade template.
Complete Example
use Ronu\RestGenericClass\Core\Controllers\RestController;
use App\Services\ProductService;
use App\Models\Product;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
class ProductController extends RestController
{
protected BaseModel|string $modelClass = Product::class;
protected BaseService|string $service;
public function __construct(ProductService $service)
{
$this->service = $service;
// Optional: Apply middleware
$this->middleware('auth:api');
$this->middleware('role:admin')->only(['destroy', 'updateMultiple']);
}
// Override store with custom validation
public function store(StoreProductRequest $request): array
{
return parent::store($request);
}
// Override update with custom validation
public function update(UpdateProductRequest $request, $id): mixed
{
return parent::update($request, $id);
}
// Add custom endpoint
public function featured(Request $request)
{
$params = $this->process_request($request);
$params['oper'] = ['and' => ['featured|=|1', 'status|=|active']];
$params['orderby'] = [['created_at' => 'desc']];
$params['pagination'] = ['page' => 1, 'pageSize' => 10];
return $this->service->list_all($params);
}
}
Next Steps