Skip to main content
This guide covers the essential CRUD operations that form the foundation of working with Rest Generic Class. You’ll learn how to set up your models, services, and controllers, then perform basic operations through the REST API.

Quick Setup

1
Define Your Model
2
Extend BaseModel and define the model constant and allowed relations:
3
<?php

namespace App\Models;

use Ronu\RestGenericClass\Core\Models\BaseModel;

class Product extends BaseModel
{
    protected $fillable = ['name', 'price', 'stock', 'category_id'];

    const MODEL = 'product';
    const RELATIONS = ['category', 'reviews'];

    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function reviews()
    {
        return $this->hasMany(Review::class);
    }
}
4
The RELATIONS constant is a security whitelist. Only relations listed here can be eager-loaded through the API.
5
Create Your Service
6
Extend BaseService and pass your model class:
7
<?php

namespace App\Services;

use App\Models\Product;
use Ronu\RestGenericClass\Core\Services\BaseService;

class ProductService extends BaseService
{
    public function __construct()
    {
        parent::__construct(Product::class);
    }
}
8
Build Your Controller
9
Extend RestController and inject your service:
10
<?php

namespace App\Http\Controllers\Api;

use App\Models\Product;
use App\Services\ProductService;
use Ronu\RestGenericClass\Core\Controllers\RestController;

class ProductController extends RestController
{
    protected $modelClass = Product::class;

    public function __construct(ProductService $service)
    {
        $this->service = $service;
    }
}
11
Register Your Routes
12
use App\Http\Controllers\Api\ProductController;

Route::prefix('v1')->group(function () {
    Route::apiResource('products', ProductController::class);
    Route::post('products/update-multiple', [ProductController::class, 'updateMultiple']);
});

Listing Records

Simple List

Get all products:
GET /api/v1/products
Response:
{
  "data": [
    {
      "id": 1,
      "name": "Wireless Mouse",
      "price": 29.99,
      "stock": 150,
      "category_id": 3
    },
    {
      "id": 2,
      "name": "USB-C Cable",
      "price": 12.99,
      "stock": 500,
      "category_id": 4
    }
  ]
}

Select Specific Fields

Reduce payload size by selecting only needed fields:
GET /api/v1/products?select=["id","name","price"]
Response:
{
  "data": [
    {
      "id": 1,
      "name": "Wireless Mouse",
      "price": 29.99
    },
    {
      "id": 2,
      "name": "USB-C Cable",
      "price": 12.99
    }
  ]
}

Load Relations

Eager-load related data to avoid N+1 queries:
GET /api/v1/products?relations=["category:id,name"]
The :id,name syntax selects only specific fields from the relation. Always include foreign keys to maintain relationships.
Response:
{
  "data": [
    {
      "id": 1,
      "name": "Wireless Mouse",
      "price": 29.99,
      "stock": 150,
      "category_id": 3,
      "category": {
        "id": 3,
        "name": "Peripherals"
      }
    }
  ]
}

Filtering

Equality Filters (Legacy)

Filter by exact matches using the attr parameter:
{
  "attr": {
    "category_id": 3,
    "status": "active"
  }
}

Dynamic Filters with oper

Use the powerful oper parameter for complex conditions:
{
  "oper": {
    "and": [
      "status|=|active",
      "price|>=|50",
      "price|<=|200"
    ]
  }
}
Supported operators:
  • Comparison: =, !=, <, >, <=, >=
  • Pattern matching: like, not like, ilike, not ilike
  • Set operations: in, not in
  • Range: between, not between
  • Null checks: null, not null
  • Date: date, not date

Combining AND and OR

{
  "oper": {
    "and": [
      "status|=|active"
    ],
    "or": [
      "category_id|=|3",
      "category_id|=|4"
    ]
  }
}

Sorting

Order results by one or more fields:
{
  "orderby": [
    {"price": "desc"},
    {"name": "asc"}
  ]
}

Pagination

Standard Pagination

{
  "pagination": {
    "page": 1,
    "pageSize": 25
  }
}
Response:
{
  "current_page": 1,
  "data": [...],
  "first_page_url": "http://api.example.com/products?page=1",
  "from": 1,
  "last_page": 4,
  "last_page_url": "http://api.example.com/products?page=4",
  "next_page_url": "http://api.example.com/products?page=2",
  "path": "http://api.example.com/products",
  "per_page": 25,
  "prev_page_url": null,
  "to": 25,
  "total": 100
}

Creating Records

Single Record

POST /api/v1/products
Content-Type: application/json

{
  "name": "Mechanical Keyboard",
  "price": 89.99,
  "stock": 50,
  "category_id": 3
}
Response:
{
  "success": true,
  "model": {
    "id": 10,
    "name": "Mechanical Keyboard",
    "price": 89.99,
    "stock": 50,
    "category_id": 3,
    "created_at": "2026-03-05T10:30:00.000000Z",
    "updated_at": "2026-03-05T10:30:00.000000Z"
  }
}

Bulk Create

Create multiple records in one request:
POST /api/v1/products
Content-Type: application/json

{
  "product": [
    {
      "name": "Mouse Pad",
      "price": 9.99,
      "stock": 200,
      "category_id": 3
    },
    {
      "name": "Webcam",
      "price": 49.99,
      "stock": 75,
      "category_id": 3
    }
  ]
}
The key must match the lowercase MODEL constant defined in your model (product in this example).

Updating Records

Single Update

PUT /api/v1/products/10
Content-Type: application/json

{
  "price": 79.99,
  "stock": 45
}
Response:
{
  "success": true,
  "model": {
    "id": 10,
    "name": "Mechanical Keyboard",
    "price": 79.99,
    "stock": 45,
    "category_id": 3,
    "created_at": "2026-03-05T10:30:00.000000Z",
    "updated_at": "2026-03-05T11:15:00.000000Z"
  }
}

Bulk Update

Update multiple records at once:
POST /api/v1/products/update-multiple
Content-Type: application/json

{
  "product": [
    {"id": 10, "stock": 40},
    {"id": 11, "stock": 0},
    {"id": 12, "price": 14.99}
  ]
}
Response:
{
  "success": true,
  "models": [
    {
      "success": true,
      "model": {"id": 10, "stock": 40, ...}
    },
    {
      "success": true,
      "model": {"id": 11, "stock": 0, ...}
    },
    {
      "success": true,
      "model": {"id": 12, "price": 14.99, ...}
    }
  ]
}
All updates in updateMultiple are wrapped in a transaction. If validation fails on any record, the entire transaction rolls back.

Deleting Records

Delete by ID

DELETE /api/v1/products/10
Response:
{
  "success": true,
  "model": {
    "id": 10,
    "name": "Mechanical Keyboard",
    ...
  }
}

Showing a Single Record

Retrieve a single record with optional relations:
GET /api/v1/products/10?relations=["category","reviews"]
Response:
{
  "id": 10,
  "name": "Mechanical Keyboard",
  "price": 79.99,
  "stock": 40,
  "category_id": 3,
  "category": {
    "id": 3,
    "name": "Peripherals"
  },
  "reviews": [
    {
      "id": 1,
      "rating": 5,
      "comment": "Excellent keyboard!"
    }
  ]
}

Common Patterns

Filtered List with Relations

Combine filtering, selection, and relation loading:
GET /api/v1/products?select=["id","name","price"]&relations=["category:id,name"]
{
  "oper": {
    "and": ["status|=|active", "price|>=|20"]
  },
  "orderby": [{"price": "asc"}],
  "pagination": {
    "page": 1,
    "pageSize": 10
  }
}

Search by Name

Use the like operator for text search:
{
  "oper": {
    "and": ["name|like|%keyboard%"]
  }
}

Get Active Products in Price Range

{
  "oper": {
    "and": [
      "status|=|active",
      "price|between|50,200"
    ]
  }
}

Next Steps

Advanced Filtering

Learn complex filtering with nested relations and multiple conditions

Relation Loading

Master eager loading and nested relation queries

Caching

Configure Redis, database, or file-based caching for better performance

Bulk Operations

Optimize multiple record operations with bulk endpoints

Evidence

  • File: src/Core/Controllers/RestController.php
    Lines: 79-102, 110-121, 158-176, 186-203, 211-230, 239-243, 251-268
    Shows the request parameter extraction and CRUD endpoints (index, store, update, updateMultiple, show, destroy)
  • File: src/Core/Services/BaseService.php
    Lines: 414-423, 611-627, 645-662, 664-676, 1057-1069, 1071-1079
    Implements list_all, create, update, update_multiple, destroy, and destroybyid methods
  • File: src/Core/Models/BaseModel.php
    Lines: 28-44
    Shows MODEL and RELATIONS constants used for configuration

Build docs developers (and LLMs) love