Search by

ronu / rest-generic-class

charlietyn

Base Class for generic restfull api in laravel

Package info

github.com/charlietyn/rest-generic-class

pkg:composer/ronu/rest-generic-class

Statistics

Installs: 715

Dependents: 1

Suggesters: 2

Stars: 6

Open Issues: 0

v3.0.0 2026-10-18 22:45 UTC

This package is auto-updated.

Last update: 2026-10-18 22:47:24 UTC


README

A Laravel package that provides base classes for RESTful CRUD with dynamic filtering, relation loading, and hierarchical listing.

Latest Version Laravel License

v3.0.0 — Route permissions moved to ronu/rest-permissions

The CRUD core no longer ships or depends on any permission code: the route permissions (catalog sync, SpatieAuthorize, the effective-permission Gate, readers, Spatie models, permission routes and the migration script) now live in ronu/rest-permissions, and Spatie is no longer a dependency of this package. The core authorizes through its contracts and Laravel's Gate. Upgrading: composer require ronu/rest-generic-class:^3.0 ronu/rest-permissions:^1.0; 2.x class names, the permissions config block and the route names keep working through the plugin. See the upgrade guide.

v2.9.0 — Authorization contracts

The CRUD core now authorizes eager loads and field writes only through Core\Contracts\Authorization (AuthorizationContext, RelationAuthorizer, FieldWriteAuthorizer) and Laravel's Gate, preparing the route permissions to move to their own package. No behaviour change with the default configuration; require_read without a request scope now fails closed. Also fixes SpatieRole::enabled_permissions() (PostgreSQL-only SQL, and an expired grant being honoured when another role held an active one). See the contracts and the release notes.

v2.8.2 — Direct user permissions without Spatie's trait

users:assign-permissions and the direct/role permission readers now work on user models without Spatie's HasPermissions/HasRoles (as generated applications are), and validation messages name the field instead of its value (an array value no longer answers 500). See the release notes.

v2.8.1 — Spatie 7/8 compatibility

SpatiePermission and SpatieRole failed to load with spatie/laravel-permission 7+ (e.g. Laravel 13.36 + Spatie 8.3.0 stopped artisan package:discover). findByName()/findOrCreate() now accept BackedEnum|string, so one release works with Spatie ^6.21 || ^7.0 || ^8.0 on Laravel 12 and 13. See the release notes.

v2.8.0 — Actions by ability and generic notation

Opt-in route attributes permission_ability / permission_requires collapse the routes of a relation into read/add/update/delete/manage actions that also require seeing the parent; grant them with admin.crm.clients.*-style patterns; the catalog syncs in batch (with --prune), and eager_relations=require_read authorizes ?relations=. See the public API, §10 and the release notes.

v2.7.1 — Fixes

GET /<channel>/modules no longer fails (with ?enabled= and the new ?format=uri), no PHP 8.4 implicit-nullable deprecations, and the channel authorization migration script now ships in scripts/.

v2.7.0 — Explicit global permissions

Opt-in explicit_globals (only routes marked with the permission_global action attribute are global; everything else fails closed), catalog_middleware to keep public routes out of the permission catalog, and the v2.5.0 legacy module-name fix. See the release notes.

v2.6.0 — Channel authorization

Optional channel prefixes, literal route permission names, fail-closed middleware, and an effective-permission Gate. Review the release notes and activation order before enabling authorization for channel-based applications.

Requirements

Laravel PHP Test runtime
12.61.1+ 8.2+ PHPUnit 11
13.12+ 8.3+ PHPUnit 12

No permission package is required. Route permissions (with spatie/laravel-permission) are provided by ronu/rest-permissions. See requirements.

The package keeps Laravel 12 support while adding Laravel 13 support. See the detailed Spanish Laravel 13 migration guide for junior developers.

Installation

composer require ronu/rest-generic-class

Publish configuration (optional)

php artisan vendor:publish --tag=rest-generic-class-config

Quickstart

1) Model

<?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);
    }
}

2) Service

<?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);
    }
}

3) Controller

<?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;
    }
}

4) Routes

use App\Http\Controllers\Api\ProductController;

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

5) Query with filtering and relations

GET /api/v1/products?select=["id","name"]&relations=["category:id,name"]
{
  "oper": {
    "and": ["status|=|active", "price|>=|50"]
  }
}

Parameter-driven aggregates

List endpoints support aggregate for global metrics and with_aggregates for per-record relationship metrics (count, sum, avg, min, max). Models opt in through HasRestAggregates. Execution uses native Laravel aggregate methods, without accepting raw SQL. See aggregation parameters and examples for authorization, filtering, pagination, cache behavior, and supported relations.

Configuration

Publish the config file and adjust values in config/rest-generic-class.php.

Environment variables:

  • LOG_LEVEL (default: debug)
  • LOG_QUERY (default: false)
  • REST_VALIDATE_COLUMNS (default: true)
  • REST_STRICT_COLUMNS (default: true)
  • REST_CACHE_ENABLED (default: false)
  • REST_CACHE_STORE (default: CACHE_STORE; supports redis, database, file, memcached, etc.)
  • REST_CACHE_TTL (default: 60 seconds)
  • REST_CACHE_TTL_LIST / REST_CACHE_TTL_ONE
  • REST_VALIDATION_CACHE_ENABLED (default: true)
  • REST_VALIDATION_CACHE_TTL (default: 3600 seconds)
  • REST_VALIDATION_CACHE_PREFIX (default: validation)
  • REST_VALIDATION_CONNECTION (default: db)

Cache strategy (Laravel native / Redis / database)

This package now supports generic cache integration via Laravel Cache stores.

How it works

  • Cache is applied in BaseService for read operations: list_all and get_one.
  • Cache key fingerprint includes: model, operation, route, query params, auth user, selected headers, normalized params, and a model cache version.
  • Any successful write (create, update, destroy, destroybyid) bumps a model-level cache version to avoid stale reads without needing tags.
  • Laravel 13's secure cache.serializable_classes=false default is honored: array payloads are cached, while paginator objects are not cached unless the host application supplies an explicit class allowlist.
  • Cache keys use the rgc:v2 namespace so incompatible Laravel 12 cache entries are not reused after an upgrade.

Store selection

Use any Laravel cache store by setting:

REST_CACHE_ENABLED=true
REST_CACHE_STORE=redis
# or: database, file, memcached, dynamodb, etc.

Request-aware behavior

You can control cache per request:

  • cache=false disables cache for that request.
  • cache_ttl=120 overrides TTL (seconds) for that request.

Example:

GET /api/v1/products?cache_ttl=120&select=["id","name"]

Multi-tenant / locale aware keys

By default cache varies by Accept-Language and X-Tenant-Id headers to avoid cross-tenant or cross-locale data leaks.

Common scenarios

1) Bulk update

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

{
  "product": [
    {"id": 10, "stock": 50},
    {"id": 11, "stock": 0}
  ]
}

2) Hierarchy listing

{
  "hierarchy": {
    "filter_mode": "with_descendants",
    "children_key": "children",
    "max_depth": 3
  }
}

3) Permission sync (Spatie, with ronu/rest-permissions)

POST /api/permissions/assign_roles
Content-Type: application/json

{
  "roles": ["admin"],
  "guard": "api",
  "mode": "SYNC",
  "perms": ["products.view", "products.create"]
}

Edge scenarios

  • Config caching hides env updates until you clear the config cache.
  • Deep hierarchy trees can time out without max_depth.
  • Excessive filter conditions trigger safety limits.

See Edge and extreme scenarios for detailed guidance.

API reference

Troubleshooting

Start with Troubleshooting for common errors such as invalid relations, operator errors, and missing optional packages.

Documentation

Contributing / Security

Please open issues and pull requests on GitHub. For security concerns, report them privately to the maintainer email listed in the package metadata.