Skip to main content

Overview

The BaseModel class is the foundation of your Laravel models when using Rest Generic Class. It extends Laravel’s Eloquent Model and adds:
  • Automatic REST integration with services and controllers
  • Role-based field restrictions via fieldsByRole
  • Hierarchical data support with self-referencing relationships
  • Relation declaration for security and filtering
  • Built-in validation with scenario-based rules

Extending BaseModel

All your models should extend BaseModel instead of Laravel’s default Model:
use Ronu\RestGenericClass\Core\Models\BaseModel;

class Product extends BaseModel
{
    protected $fillable = ['name', 'price', 'stock', 'category_id', 'status'];
    
    const MODEL = 'product';
    const RELATIONS = ['category', 'reviews'];
    
    // Define your Eloquent relations
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
    
    public function reviews()
    {
        return $this->hasMany(Review::class);
    }
}

Required Constants

MODEL

Defines the model name used in API requests:
const MODEL = 'product';
This allows batch operations like:
{
  "product": [
    {"name": "Item 1", "price": 100},
    {"name": "Item 2", "price": 200}
  ]
}

RELATIONS

Declares which relations are allowed for eager loading and filtering. This is critical for security — only relations listed here can be loaded via the relations parameter.
const RELATIONS = ['category', 'reviews', 'tags'];
If const RELATIONS is not defined and strict_relations is enabled (default), requests with relations parameter will throw a 500 error. Always declare your relations explicitly.
Now clients can request:
GET /api/products?relations=["category","reviews"]

HIERARCHY_FIELD_ID (Optional)

Enables hierarchical queries for self-referencing models:
const HIERARCHY_FIELD_ID = 'parent_id';
This activates:
  • Hierarchical listing with the hierarchy parameter
  • Helper methods: hierarchyParent(), hierarchyChildren()
  • Tree-building capabilities
Example for categories with parent-child structure:
class Category extends BaseModel
{
    protected $fillable = ['name', 'parent_id'];
    
    const MODEL = 'category';
    const RELATIONS = [];
    const HIERARCHY_FIELD_ID = 'parent_id';
}
See Hierarchical Data for full details.

Role-Based Field Access Control

The fieldsByRole property enables fine-grained field-level access control using Spatie roles.

Basic Usage

class User extends BaseModel
{
    protected $fillable = [
        'name', 'email', 'password',
        'is_superuser', 'permissions', 'status', 'role_id'
    ];
    
    protected array $fieldsByRole = [
        'superadmin' => ['is_superuser', 'permissions'],
        'admin'      => ['status', 'role_id'],
    ];
}

How It Works

Fields NOT listed in fieldsByRole are writable by any authenticated user (base fields). Fields listed under a role are “privileged” and require that specific role:
  • is_superuser, permissions → Only superadmin role can write
  • status, role_id → Only admin role can write
  • name, email, password → Any authenticated user can write (not restricted)
fieldsByRole
array<string, list<string>>
Maps Spatie role names to field names. Users without the required role will have those fields stripped from incoming requests and marked as prohibited in validation.

Resolution Logic

The getDeniedFieldsForUser() method returns fields the user cannot write:
  1. If fieldsByRole is empty → [] (no restrictions)
  2. If user->is_superuser === true[] (unrestricted)
  3. Otherwise:
    • Universe = all fields mentioned in fieldsByRole
    • Allowed = fields the user CAN write (from their roles)
    • Denied = Universe − Allowed
From BaseModel.php:111-138:
public function getDeniedFieldsForUser(mixed $user): array
{
    // Fast path: no restrictions declared
    if (empty($this->fieldsByRole)) {
        return [];
    }
    
    // Fast path: superuser bypasses all restrictions
    if ($user->is_superuser ?? false) {
        return [];
    }
    
    // Build universe of all privileged fields
    $allPrivileged = array_unique(
        array_merge(...array_values($this->fieldsByRole))
    );
    
    // Collect fields user CAN write via their roles
    $allowed = [];
    foreach ($this->fieldsByRole as $role => $fields) {
        if (method_exists($user, 'hasRole') && $user->hasRole($role)) {
            $allowed = array_merge($allowed, $fields);
        }
    }
    
    // Return denied = universe - allowed
    return array_values(array_diff($allPrivileged, $allowed));
}
This method is consumed by:
  • FilterRequestByRole middleware → strips denied fields from request payload
  • BaseRequest::mergeProhibitedRules() → adds prohibited validation rules

Scenario-Based Validation

Models support scenario-based validation rules:
protected function rules(string $scenario): array
{
    $base = [
        'name' => 'required|string|max:255',
        'email' => 'required|email',
    ];
    
    if ($scenario === 'create') {
        $base['password'] = 'required|min:8';
    }
    
    if ($scenario === 'update') {
        $base['password'] = 'sometimes|min:8';
    }
    
    return $base;
}
Scenarios are automatically set by the service layer:
  • create for new records
  • update for existing records

Hierarchy Helper Methods

When HIERARCHY_FIELD_ID is defined, BaseModel provides:
hasHierarchyField()
bool
Returns true if the model has HIERARCHY_FIELD_ID defined.
hierarchyParent()
BelongsTo|null
Returns a belongsTo relation to the parent record.
hierarchyChildren()
HasMany
Returns a hasMany relation to child records.
isHierarchyRoot()
bool
Returns true if the record has no parent (root node).
getHierarchyAncestors()
Collection
Returns all ancestors from parent to root.
getHierarchyDescendants(?int $maxDepth)
Collection
Returns all descendants. Optional $maxDepth limits recursion depth.

MongoDB Relations

BaseModel includes MongoDB relationship helpers for cross-database relations:
public function belongsToMongo(
    string $related,
    ?string $foreignKey = null,
    ?string $ownerKey = null,
    ?string $relation = null
): MongoBelongTo

public function hasManyMongo(
    string $related,
    ?string $foreignKey = null,
    ?string $localKey = null
): MongoHasMany

public function hasOneMongo(
    string $related,
    ?string $foreignKey = null,
    ?string $localKey = null
): HasOneOrMany

Complete Example

use Ronu\RestGenericClass\Core\Models\BaseModel;
use Illuminate\Database\Eloquent\SoftDeletes;

class Product extends BaseModel
{
    use SoftDeletes;
    
    protected $fillable = [
        'name', 'description', 'price', 'stock',
        'category_id', 'status', 'featured', 'internal_notes'
    ];
    
    const MODEL = 'product';
    const RELATIONS = ['category', 'reviews', 'tags'];
    const columns = ['id', 'name', 'price', 'stock', 'status'];
    
    // Privilege system: only admins can modify these fields
    protected array $fieldsByRole = [
        'admin' => ['featured', 'internal_notes'],
    ];
    
    // Scenario-based validation
    protected function rules(string $scenario): 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',
        ];
    }
    
    // Eloquent relations (must match RELATIONS constant)
    public function category()
    {
        return $this->belongsTo(Category::class);
    }
    
    public function reviews()
    {
        return $this->hasMany(Review::class);
    }
    
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}

Next Steps

Build docs developers (and LLMs) love