Primordyx Framework Documentation

SafeModel extends Model
in package

Database Model for Safe Session Storage and Management

Provides database persistence layer for the Safe session management system with comprehensive CRUD operations, automatic expiration handling, and JSON content serialization. Designed to work seamlessly with the Safe class for secure, database-backed session storage.

Key Features

  • JSON Content Storage: Automatic serialization/deserialization of session data
  • Expiration Management: Built-in expiration checking and extension methods
  • Soft Delete Support: Safe removal with restoration capabilities
  • Timestamp Tracking: Automatic created/updated timestamp management
  • Type Safety: Proper casting for datetime fields and content validation
  • Safe ID Lookup: Efficient queries by cryptographic session identifiers

Database Schema

The model expects a 'safes' table with these columns:

  • id (Primary Key): Auto-incrementing database ID
  • safe_id (Unique): Cryptographically secure session identifier (64 chars)
  • contents (JSON/Text): Serialized session data as JSON string
  • expires_at (DateTime): Session expiration timestamp
  • created_at (DateTime): Record creation timestamp
  • updated_at (DateTime): Last modification timestamp
  • deleted_at (DateTime): Soft delete timestamp (nullable)
  • restored_at (DateTime): Restoration timestamp (nullable)

Usage Patterns

// Find session by safe ID
$safe = SafeModel::findBySafeId($sessionId);

// Check expiration and extend if needed
if ($safe->isExpired()) {
    $safe->extend(3600); // Extend 1 hour
}

// Work with session contents
$data = $safe->getContents();
$data['new_key'] = 'value';
$safe->setContents($data);
$safe->save();

Integration

  • Used internally by SafePersistenceInterface implementations
  • Integrates with Safe class for session lifecycle management
  • Supports DatabaseSafePersistence for production session storage
  • Compatible with existing Model framework patterns and conventions
Tags
since
1.0.0

Table of Contents

Properties

$casts  : array<string|int, mixed>
The attributes that should be cast to native types.
$data  : array<string, mixed>
Current model attribute data with type casting applied
$db  : PDO|null
PDO database connection handle for all database operations
$errors  : array<string, array<string|int, string>>
Validation and database error collection
$includeSoftDeleted  : bool
Flag to include soft-deleted records in queries
$original  : array<string, mixed>
Original attribute values for change detection
$primaryKey  : string
The primary key for the model.
$queryBuilder  : QueryBuilder|null
Current QueryBuilder instance for fluent query chaining
$softDelete  : bool
Indicates if the model should use soft deletes.
$table  : string
The database table associated with the model.
$timestamps  : bool
Indicates if the model should be timestamped.

Methods

__construct()  : mixed
Initialize model with database introspection and attribute setup
__get()  : mixed
Magic method to retrieve model attributes
__isset()  : bool
Magic method to check if model attribute is set
__set()  : void
Magic method to set model attributes with type casting
andWhere()  : static
Alias for where() method providing explicit AND condition semantics
asArray()  : array<string, mixed>
Alias for toArray() method providing consistent naming
bulkUpdate()  : int
Perform bulk UPDATE operation on records matching current query conditions
delete()  : bool
Remove record using soft delete or hard delete based on model configuration
exists()  : bool
Check if model represents existing database record
extend()  : bool
Extend the safe session expiration by specified duration
fill()  : static
Populate model attributes from associative array with type casting
find()  : static|null
Find single record by primary key and populate current model instance
findAll()  : array<string|int, static>
Retrieve all records from table as array of model instances
findBySafeId()  : static|null
Find safe session by cryptographic session identifier
findPage()  : Paginator
Alias for paginate() with consistent naming
findPageAsModels()  : Paginator
Alias for paginateAsModels() with consistent naming
first()  : static|null
Execute query and return first matching record as model instance
forceDelete()  : bool
Permanently delete record from database regardless of soft delete setting
getAsModels()  : array<string|int, static>
Execute current query and return results as model instances
getContents()  : array<string, mixed>
Retrieve and deserialize safe session contents as associative array
getDirty()  : array<string, mixed>
Get associative array of fields that have changed from original values
getErrors()  : array<string, array<string|int, string>>
Retrieve all validation and database errors as associative array
hasErrors()  : bool
Check if model has any validation or database errors
isDirty()  : bool
Check if model has unsaved changes compared to original database state
isExpired()  : bool
Check if the safe session has exceeded its expiration time
isValid()  : bool
Validate model data against defined rules and populate error array
jsonSerialize()  : array<string, mixed>
JsonSerializable interface implementation for automatic JSON conversion
limit()  : static
Add LIMIT clause to restrict number of returned records
nextPageUrl()  : string|null
Generate URL for next page in pagination sequence
offset()  : static
Add OFFSET clause to skip records at beginning of result set
onlyTrashed()  : array<string|int, array<string, mixed>>
Get only soft-deleted records from the database
orderBy()  : static
Add ORDER BY clause to query with column and direction
orderByRaw()  : static
Add raw SQL ORDER BY clause to query
orWhere()  : static
Add OR WHERE condition to query builder
paginate()  : Paginator
Execute query with pagination and return raw data results
paginateAsModels()  : Paginator
Execute query with pagination and return model instance results
prevPageUrl()  : string|null
Generate URL for previous page in pagination sequence
query()  : QueryBuilder
Create new QueryBuilder instance with automatic soft delete filtering
reloadColumns()  : void
Force reload of table column information from database
resetQueryBuilder()  : static
Clear current query builder state for fresh query building
restore()  : bool
Restore soft-deleted record by clearing deleted_at timestamp
rules()  : array<string, string>
Define validation rules for model attributes
save()  : bool
Persist model changes to database with validation and timestamp management
setContents()  : void
Serialize and store session data array as JSON in contents field
toArray()  : array<string, mixed>
Convert model data to associative array
where()  : static
Add WHERE condition to query builder with flexible parameter support
withTrashed()  : static
Include soft-deleted records in subsequent queries
castAttribute()  : mixed
Apply configured type casting to attribute values
insertRow()  : bool
Insert new record into database with column filtering and tracking
table()  : string
Get the table name for this model with automatic generation fallback
updateRow()  : bool
Update existing database record with only changed fields

Properties

$casts

The attributes that should be cast to native types.

protected array<string|int, mixed> $casts = ['expires_at' => 'datetime', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'deleted_at' => 'datetime', 'restored_at' => 'datetime']
Tags
since
1.0.0

$data

Current model attribute data with type casting applied

protected array<string, mixed> $data = []

Stores all model attributes as key-value pairs after type casting via castAttribute(). Initialized with all table columns set to null during construction. Modified via magic methods __get/__set or direct array access.

Model attributes with applied type casting

Tags
since
1.0.0

$db

PDO database connection handle for all database operations

protected PDO|null $db = null

Database connection used for all SQL operations. Defaults to ConnectionManager global connection but can be overridden via constructor parameter for multi-database scenarios or testing with specific connections.

Database connection or null to use default

Tags
since
1.0.0

$errors

Validation and database error collection

protected array<string, array<string|int, string>> $errors = []

Stores validation errors from isValid() and database operation errors. Cleared on each validation run. Used by hasErrors() and getErrors() methods to provide error feedback after validation or save operations.

Error messages grouped by field/category

Tags
since
1.0.0

$includeSoftDeleted

Flag to include soft-deleted records in queries

protected bool $includeSoftDeleted = false

When true, queries will include records with deleted_at timestamps. Modified by withTrashed() method to temporarily include soft-deleted records in query results. Resets to false for new queries.

Whether current query includes soft-deleted records

Tags
since
1.0.0

$original

Original attribute values for change detection

protected array<string, mixed> $original = []

Preserves the initial state of model data after loading from database or after successful save operations. Used by isDirty() and getDirty() methods to detect field changes and optimize update queries.

Original model state for change tracking

Tags
since
1.0.0

$primaryKey

The primary key for the model.

protected string $primaryKey = 'id'
Tags
since
1.0.0

$queryBuilder

Current QueryBuilder instance for fluent query chaining

protected QueryBuilder|null $queryBuilder = null

Preserves query state between fluent method calls. Set by query methods like where(), orderBy(), limit() to maintain query context. Reset after query execution or via resetQueryBuilder() method.

Current query builder or null for fresh queries

Tags
since
1.0.0

$softDelete

Indicates if the model should use soft deletes.

protected bool $softDelete = true
Tags
since
1.0.0

$table

The database table associated with the model.

protected string $table = 'safes'
Tags
since
1.0.0

$timestamps

Indicates if the model should be timestamped.

protected bool $timestamps = true
Tags
since
1.0.0

Methods

__construct()

Initialize model with database introspection and attribute setup

public __construct([array<string, mixed> $attrs = [] ][, PDO|null $db = null ][, string|null $table = null ][, string|null $primaryKey = null ]) : mixed

Creates new model instance with automatic database column discovery, caching, and attribute initialization. Performs database introspection to determine available columns and initializes all columns to null before applying provided attributes.

Initialization Process

  1. Sets database connection (parameter or ConnectionManager default)
  2. Overrides table/primaryKey if provided
  3. Checks Cargo cache for table columns
  4. Performs DESCRIBE query if columns not cached
  5. Caches column list for future use
  6. Initializes all columns to null in $data array
  7. Applies provided attributes via fill()
  8. Captures original state for change tracking

Column Caching

Uses Cargo caching with key format: 'model.columns.{table_name}' Reduces database queries by caching DESCRIBE results Automatically handles cache invalidation via reloadColumns()

Parameters
$attrs : array<string, mixed> = []

Initial attribute values to fill

$db : PDO|null = null

Database connection override (null uses ConnectionManager)

$table : string|null = null

Table name override (null uses model default)

$primaryKey : string|null = null

Primary key override (null uses model default)

Tags
throws
RuntimeException

If database introspection fails or table doesn't exist

since
1.0.0
example

Basic Construction

$user = new User(['name' => 'John', 'email' => 'john@example.com']);
example

With Database Override

$readOnlyDb = ConnectionManager::getHandle('readonly');
$user = new User([], $readOnlyDb);
example

With Table Override

$user = new User(['name' => 'John'], null, 'custom_users_table');
see
ConnectionManager::getHandle()

For database connection management

see
Cargo

For column caching system

see
QueryTracker

For query performance monitoring

__get()

Magic method to retrieve model attributes

public __get(string $key) : mixed

Provides object-like access to model data array. Returns null for non-existent attributes rather than throwing errors for graceful handling of undefined properties.

Parameters
$key : string

Attribute name to retrieve

Tags
since
1.0.0
example

Attribute Access

$user = new User(['name' => 'John', 'email' => 'john@example.com']);
echo $user->name;  // 'John'
echo $user->missing; // null
Return values
mixed

Attribute value or null if not set

__isset()

Magic method to check if model attribute is set

public __isset(string $key) : bool

Returns true if the attribute exists and is not null in the data array. Enables use of isset() and empty() with model attributes.

Parameters
$key : string

Attribute name to check

Tags
since
1.0.0
example

Existence Checking

if (isset($user->email)) {
    sendEmail($user->email);
}
Return values
bool

True if attribute is set and not null

__set()

Magic method to set model attributes with type casting

public __set(string $key, mixed $value) : void

Applies configured type casting via castAttribute() before storing values in the data array. Provides object-like attribute assignment with automatic type conversion.

Parameters
$key : string

Attribute name to set

$value : mixed

Raw value to cast and store

Tags
since
1.0.0
example

Attribute Setting

$user = new User();
$user->active = 'yes';  // Casted to 1 if 'active' => 'bool' in $casts
$user->age = '25';      // Casted to 25 if 'age' => 'int' in $casts
see
castAttribute()

For type casting implementation

andWhere()

Alias for where() method providing explicit AND condition semantics

public andWhere(mixed ...$args) : static

Functionally identical to where() but provides clearer intent when building complex queries with multiple conditions. All WHERE conditions are combined with AND by default.

Parameters
$args : mixed

Variable arguments for WHERE condition

Tags
since
1.0.0
example

Explicit AND Conditions

$users = (new User())
    ->where('active', 1)
    ->andWhere('verified', 1)
    ->andWhere('age', '>=', 18)
    ->getAsModels();
see
where()

Primary implementation

Return values
static

Current model instance for method chaining

asArray()

Alias for toArray() method providing consistent naming

public asArray() : array<string, mixed>

Identical functionality to toArray() with alternative method name for consistency across different coding styles and preferences.

Tags
since
1.0.0
see
toArray()

Primary implementation

Return values
array<string, mixed>

All model attributes as associative array

bulkUpdate()

Perform bulk UPDATE operation on records matching current query conditions

public bulkUpdate(array<string, mixed> $data) : int

Updates multiple records with provided data based on current QueryBuilder conditions. Applies automatic timestamp management and type casting. Resets query builder after execution and returns number of affected rows.

Update Process

  1. Validates data array is not empty
  2. Gets WHERE conditions from current QueryBuilder
  3. Adds updated_at timestamp if timestamps enabled
  4. Applies type casting to all values
  5. Builds and executes UPDATE SQL
  6. Returns count of affected rows
  7. Resets QueryBuilder state

Performance Benefits

  • Single SQL operation vs multiple save() calls
  • Reduced database round trips
  • Automatic WHERE clause building from query conditions
  • Consistent type casting and timestamp management
Parameters
$data : array<string, mixed>

Field values to update across matched records

Tags
since
1.0.0
example

Bulk Status Update

$affected = (new User())
    ->where('active', 0)
    ->where('last_login', '<', '2023-01-01')
    ->bulkUpdate(['status' => 'inactive', 'archived' => 1]);

echo "Updated $affected inactive users";
example

Conditional Bulk Updates

// Promote active users to premium
$count = (new User())
    ->where('active', 1)
    ->where('purchases', '>', 5)
    ->bulkUpdate(['tier' => 'premium', 'discount' => 10]);
see
QueryBuilder::getWhereClause()

For WHERE condition extraction

see
castAttribute()

For value type casting

Return values
int

Number of database rows affected by update

delete()

Remove record using soft delete or hard delete based on model configuration

public delete() : bool

Removes record from active dataset using either soft delete (sets deleted_at) or hard delete (removes from database) based on $softDelete property setting. Returns false if model doesn't have primary key value.

Soft Delete Behavior

  • Sets deleted_at timestamp to current datetime
  • Preserves record in database for recovery
  • Automatically excluded from future queries
  • Enables restore() functionality

Hard Delete Behavior

  • Permanently removes record from database
  • Cannot be recovered after deletion
  • Used when $softDelete = false
Tags
since
1.0.0
example

Soft Delete (Default)

$user = (new User())->find(123);
$user->delete(); // Sets deleted_at timestamp

// User still exists but excluded from queries
$found = (new User())->find(123); // Returns null
$withDeleted = (new User())->withTrashed()->find(123); // Returns user
example

Hard Delete

class TemporaryModel extends Model {
    protected bool $softDelete = false;
}

$temp = (new TemporaryModel())->find(123);
$temp->delete(); // Permanently removes from database
see
forceDelete()

For permanent deletion regardless of soft delete setting

see
restore()

For recovering soft-deleted records

Return values
bool

True if deletion successful, false if no primary key or database error

exists()

Check if model represents existing database record

public exists() : bool

Determines if current model instance represents an existing database record by checking for non-empty primary key value. Used internally by save() to decide between INSERT and UPDATE operations.

Tags
since
1.0.0
Return values
bool

True if primary key has value (existing record), false if empty (new record)

extend()

Extend the safe session expiration by specified duration

public extend([int $seconds = 3600 ]) : bool

Updates the expires_at timestamp to extend the session lifetime by the given number of seconds from the current time. Automatically saves the updated expiration to the database.

Extension Behavior

  • Calculates new expiration as current time + seconds parameter
  • Overwrites existing expiration (does not add to existing time)
  • Automatically saves the updated record to database
  • Uses Y-m-d H:i:s format for database compatibility
Parameters
$seconds : int = 3600

Number of seconds to extend from current time (default: 3600 = 1 hour)

Tags
throws
Exception

If database save operations fail

since
1.0.0
example
$safe = SafeModel::findBySafeId($sessionId);

// Extend session by 2 hours
if ($safe->extend(7200)) {
    echo "Session extended successfully";
}

// Use default 1-hour extension
$safe->extend();
Return values
bool

True if extension and database save succeeded, false on failure

fill()

Populate model attributes from associative array with type casting

public fill(array<string, mixed> $attrs) : static

Fills model data array with provided attributes, applying configured type casting to each value. Enables mass assignment of attributes with proper type conversion and validation.

Parameters
$attrs : array<string, mixed>

Associative array of attribute values

Tags
since
1.0.0
example

Mass Assignment

$user = (new User())->fill([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'active' => '1',  // Casted to boolean if configured
    'age' => '25'     // Casted to integer if configured
]);
example

From Form Data

$user = (new User())->fill($_POST);
$user->save();
see
castAttribute()

For type casting implementation

Return values
static

Current model instance for method chaining

find()

Find single record by primary key and populate current model instance

public find(mixed $id) : static|null

Loads record data into the current model instance and updates original state for change tracking. Returns the model instance for method chaining or null if record not found.

State Management

  • Populates $data array with record fields
  • Updates $original array for change tracking
  • Enables immediate save() operations after modifications
  • Respects soft delete settings
Parameters
$id : mixed

Primary key value to search for

Tags
since
1.0.0
example

Single Record Loading

$user = (new User())->find(123);
if ($user) {
    $user->name = 'Updated Name';
    $user->save(); // Updates existing record
}
example

Method Chaining

$user = (new User())->find(123)?->updateLastLogin();
Return values
static|null

Current model instance if found, null if not found

findAll()

Retrieve all records from table as array of model instances

public findAll() : array<string|int, static>

Loads all non-soft-deleted records from the model's table and returns them as an array of new model instances. Each instance is fully populated and ready for individual operations.

Tags
since
1.0.0
example

Loading All Records

$allUsers = (new User())->findAll();
foreach ($allUsers as $user) {
    echo $user->name . "\n";
    $user->updateLastSeen();
}
example

With Soft Delete Context

$allUsers = (new User())->withTrashed()->findAll(); // Includes deleted
$activeOnly = (new User())->findAll(); // Excludes deleted (default)
Return values
array<string|int, static>

Array of model instances for all records

findBySafeId()

Find safe session by cryptographic session identifier

public static findBySafeId(string $safeId) : static|null

Queries the database for a safe record using the cryptographically secure session ID rather than the auto-incrementing database primary key. This is the primary lookup method for active session retrieval.

Query Behavior

  • Searches by 'safe_id' column (not 'id' primary key)
  • Returns first matching active (non-soft-deleted) record
  • Automatically applies model's soft delete filtering
  • Uses framework's standard query builder methods
Parameters
$safeId : string

The cryptographic session identifier (64-character hex string)

Tags
since
1.0.0
example
$sessionId = 'a1b2c3d4...'; // 64-char session ID
$safe = SafeModel::findBySafeId($sessionId);

if ($safe && !$safe->isExpired()) {
    $sessionData = $safe->getContents();
}
Return values
static|null

SafeModel instance if found, null if session doesn't exist or expired

findPage()

Alias for paginate() with consistent naming

public findPage([int $perPage = 15 ][, int $page = 1 ]) : Paginator

Provides alternative method name for pagination with raw data arrays. Identical functionality to paginate() with more explicit naming.

Parameters
$perPage : int = 15

Number of records per page

$page : int = 1

Current page number (1-based)

Tags
since
1.0.0
see
paginate()

Primary implementation

Return values
Paginator

Pagination results with raw data

findPageAsModels()

Alias for paginateAsModels() with consistent naming

public findPageAsModels([int $perPage = 15 ][, int $page = 1 ]) : Paginator

Provides alternative method name for pagination with model instances. Identical functionality to paginateAsModels() with more explicit naming.

Parameters
$perPage : int = 15

Number of records per page

$page : int = 1

Current page number (1-based)

Tags
since
1.0.0
see
paginateAsModels()

Primary implementation

Return values
Paginator

Pagination results with model instances

first()

Execute query and return first matching record as model instance

public first() : static|null

Returns the first record matching current query conditions as a populated model instance. Returns null if no records match the query conditions. Automatically applies LIMIT 1 for performance optimization.

Tags
since
1.0.0
example

First Matching Record

$admin = (new User())
    ->where('role', 'admin')
    ->where('active', 1)
    ->first();

if ($admin) {
    echo "Admin found: " . $admin->name;
}
example

With Ordering

$newest = (new User())
    ->orderBy('created_at', 'DESC')
    ->first(); // Most recently created user
Return values
static|null

Model instance for first matching record or null if none found

forceDelete()

Permanently delete record from database regardless of soft delete setting

public forceDelete() : bool

Performs hard DELETE operation that permanently removes record from database. Bypasses soft delete configuration and cannot be recovered. Use with caution as this operation is irreversible.

Tags
since
1.0.0
example

Permanent Deletion

$user = (new User())->find(123);
$user->forceDelete(); // Permanently removes from database

// Record no longer exists
$gone = (new User())->withTrashed()->find(123); // Returns null
example

Cleanup Deleted Records

// Permanently remove old soft-deleted records
$oldDeleted = (new User())->onlyTrashed();
foreach ($oldDeleted as $userData) {
    if (strtotime($userData['deleted_at']) < strtotime('-30 days')) {
        $user = (new User())->fill($userData);
        $user->forceDelete(); // Permanent cleanup
    }
}
see
delete()

For soft delete option

Return values
bool

True if permanent deletion successful, false if no primary key or database error

getAsModels()

Execute current query and return results as model instances

public getAsModels() : array<string|int, static>

Runs the built query (or basic select if no query builder) and converts each result row into a new model instance. Provides object-oriented access to result data with all model functionality.

Tags
since
1.0.0
example

Getting Model Objects

$activeUsers = (new User())
    ->where('active', 1)
    ->orderBy('name')
    ->getAsModels();

foreach ($activeUsers as $user) {
    echo $user->name; // Object property access
    $user->last_login = date('Y-m-d H:i:s');
    $user->save(); // Model methods available
}
Return values
array<string|int, static>

Array of model instances from query results

getContents()

Retrieve and deserialize safe session contents as associative array

public getContents() : array<string, mixed>

Decodes the JSON-serialized session data stored in the contents field and returns it as a PHP associative array. Handles empty or malformed JSON gracefully by returning empty array.

Deserialization Process

  • Returns empty array if contents field is null or empty
  • Uses json_decode with associative array flag for PHP compatibility
  • Returns empty array if JSON is malformed or invalid
  • Preserves nested arrays and objects from original session data

Data Types

  • All data types serializable by json_encode are supported
  • Complex objects are converted to associative arrays
  • Maintains type information for strings, numbers, booleans, nulls
Tags
since
1.0.0
example
$safe = SafeModel::findBySafeId($sessionId);
$sessionData = $safe->getContents();

// Access session data
$userId = $sessionData['user_id'] ?? null;
$preferences = $sessionData['preferences'] ?? [];

// Safely check for keys
if (isset($sessionData['cart_items'])) {
    processShoppingCart($sessionData['cart_items']);
}
Return values
array<string, mixed>

Deserialized session data or empty array if no valid content

getDirty()

Get associative array of fields that have changed from original values

public getDirty() : array<string, mixed>

Returns key-value pairs of only the fields that differ between current model data and original database state. Useful for partial updates and change logging.

Tags
since
1.0.0
example

Track Changes

$user = (new User())->find(123);
$user->name = 'New Name';
$user->email = 'new@email.com';

$changes = $user->getDirty();
// ['name' => 'New Name', 'email' => 'new@email.com']

foreach ($changes as $field => $newValue) {
    echo "$field changed to: $newValue\n";
}
example

Conditional Updates

$user = (new User())->find(123);
$user->fill($_POST);

$changes = $user->getDirty();
if (!empty($changes)) {
    auditLog('User updated', $changes);
    $user->save();
}
see
isDirty()

For checking if changes exist

Return values
array<string, mixed>

Changed fields with their current values

getErrors()

Retrieve all validation and database errors as associative array

public getErrors() : array<string, array<string|int, string>>

Returns complete error array with field names as keys and error message arrays as values. Includes both validation errors from isValid() and database operation errors from save/delete operations.

Tags
since
1.0.0
example

Error Display

$user = new User();
$user->save();

foreach ($user->getErrors() as $field => $messages) {
    echo "Field '$field': " . implode(', ', $messages) . "\n";
}
example

Form Validation

$user = new User($_POST);
if (!$user->save()) {
    $errors = $user->getErrors();
    renderForm($_POST, $errors);
}
see
hasErrors()

For checking error existence

see
isValid()

For validation process

Return values
array<string, array<string|int, string>>

Error messages grouped by field/category

hasErrors()

Check if model has any validation or database errors

public hasErrors() : bool

Returns true if the errors array contains any error messages from validation failures or database operation errors. Useful for conditional logic based on model error state.

Tags
since
1.0.0
example

Error State Checking

$user = new User(['email' => 'invalid']);
$user->save();

if ($user->hasErrors()) {
    displayErrorMessages($user->getErrors());
} else {
    redirectToSuccess();
}
see
getErrors()

For retrieving error details

see
isValid()

For validation process

Return values
bool

True if errors exist, false if error-free

isDirty()

Check if model has unsaved changes compared to original database state

public isDirty([string|null $field = null ]) : bool

Compares current model data with original values to detect modifications. Can check entire model or specific field for changes. Returns true if any differences are found between current and original state.

Parameters
$field : string|null = null

Specific field to check, or null for entire model

Tags
since
1.0.0
example

Check Entire Model

$user = (new User())->find(123);
$user->name = 'New Name';

if ($user->isDirty()) {
    echo "User has unsaved changes";
    $user->save();
}
example

Check Specific Field

$user = (new User())->find(123);
$user->name = 'New Name';
$user->email = 'new@email.com';

if ($user->isDirty('name')) {
    echo "Name was changed";
}
see
getDirty()

For retrieving changed fields and values

Return values
bool

True if model/field has unsaved changes, false if unchanged

isExpired()

Check if the safe session has exceeded its expiration time

public isExpired() : bool

Compares the session's expiration timestamp against the current time to determine if the session should be considered expired. Sessions without expiration timestamps are treated as expired for security.

Expiration Logic

  • Returns true if expires_at is null or empty (safety default)
  • Returns true if expires_at timestamp is less than or equal to current time
  • Returns false only if session has valid future expiration time
  • Uses PHP's DateTime for accurate timestamp comparison
Tags
throws
Exception

If DateTime operations fail or expires_at format is invalid

since
1.0.0
example
$safe = SafeModel::findBySafeId($sessionId);

if ($safe->isExpired()) {
    // Session expired - redirect to login
    $safe->delete();
} else {
    // Session valid - continue processing
    $safe->extend(); // Optional: extend expiration
}
Return values
bool

True if session is expired and should not be used, false if still valid

isValid()

Validate model data against defined rules and populate error array

public isValid() : bool

Runs model data through Validator system using rules from rules() method. Clears previous errors and populates $errors array with any validation failures. Returns true only if all validations pass.

Validation Process

  1. Clears existing error array
  2. Gets validation rules from rules() method
  3. Runs Validator::validate() on current data
  4. Populates $errors array with failures
  5. Returns true if no errors found
Tags
since
1.0.0
example

Pre-Save Validation

$user = new User(['email' => 'invalid-email']);

if ($user->isValid()) {
    $user->save();
} else {
    foreach ($user->getErrors() as $field => $messages) {
        echo "$field: " . implode(', ', $messages) . "\n";
    }
}
see
rules()

For validation rule definition

see
getErrors()

For accessing validation errors

see
Validator::validate()

For validation implementation

Return values
bool

True if all validations pass, false if any validation fails

jsonSerialize()

JsonSerializable interface implementation for automatic JSON conversion

public jsonSerialize() : array<string, mixed>

Enables automatic JSON serialization when model is passed to json_encode(). Returns model data array for consistent JSON representation of model objects in API responses and data serialization.

Tags
since
1.0.0
example

Automatic JSON Conversion

$user = (new User())->find(123);
echo json_encode($user); // Automatically calls jsonSerialize()
example

API Response

header('Content-Type: application/json');
echo json_encode([
    'user' => $user,           // Automatically serialized
    'status' => 'success'
]);
see
JsonSerializable

Interface specification

see
toArray()

For manual array conversion

Return values
array<string, mixed>

Model data for JSON serialization

limit()

Add LIMIT clause to restrict number of returned records

public limit(int $limit) : static

Limits query results to specified number of records. Commonly used with orderBy() for top-N queries or with offset() for pagination.

Parameters
$limit : int

Maximum number of records to return

Tags
since
1.0.0
example

Top Records

$topUsers = (new User())
    ->orderBy('points', 'DESC')
    ->limit(10)
    ->getAsModels();
example

With Pagination

$page2Users = (new User())
    ->orderBy('id')
    ->limit(20)
    ->offset(20)  // Skip first 20 records
    ->getAsModels();
see
offset()

For pagination support

see
paginate()

For full pagination functionality

Return values
static

Current model instance for method chaining

nextPageUrl()

Generate URL for next page in pagination sequence

public nextPageUrl(int $perPage, int $currentPage, int $totalPages, string $baseUrl[, array<string, mixed> $extraParams = [] ]) : string|null

Creates properly formatted URL for the next page with pagination parameters and optional additional query parameters. Returns null if already on last page.

Parameters
$perPage : int

Items per page for URL parameters

$currentPage : int

Current page number for calculation

$totalPages : int

Total available pages for boundary checking

$baseUrl : string

Base URL without query parameters

$extraParams : array<string, mixed> = []

Additional query parameters to include

Tags
since
1.0.0
example

Navigation URL Generation

$nextUrl = $user->nextPageUrl(20, 2, 5, '/users', ['status' => 'active']);
// Result: '/users?page=3&limit=20&status=active'
Return values
string|null

Next page URL or null if on last page

offset()

Add OFFSET clause to skip records at beginning of result set

public offset(int $offset) : static

Skips specified number of records before returning results. Primarily used with limit() for manual pagination implementation. Results depend on consistent ordering for predictable pagination.

Parameters
$offset : int

Number of records to skip

Tags
since
1.0.0
example

Manual Pagination

// Page 3 with 15 records per page
$users = (new User())
    ->orderBy('id')
    ->limit(15)
    ->offset(30)  // Skip first 30 records (2 pages × 15)
    ->getAsModels();
see
limit()

For result count restriction

see
paginate()

For automated pagination

Return values
static

Current model instance for method chaining

onlyTrashed()

Get only soft-deleted records from the database

public onlyTrashed() : array<string|int, array<string, mixed>>

Returns array of records that have been soft-deleted (deleted_at IS NOT NULL). Provides direct access to deleted records without affecting model query state.

Tags
since
1.0.0
example

Retrieving Deleted Records

$deletedUsers = (new User())->onlyTrashed();
foreach ($deletedUsers as $userData) {
    echo "Deleted user: " . $userData['name'];
}
see
withTrashed()

To include deleted records in normal queries

see
restore()

To restore soft-deleted records

Return values
array<string|int, array<string, mixed>>

Array of deleted record data

orderBy()

Add ORDER BY clause to query with column and direction

public orderBy(string $column[, string $direction = 'ASC' ]) : static

Sorts query results by specified column and direction. Preserves query state and allows multiple orderBy calls for complex sorting requirements.

Parameters
$column : string

Database column name to sort by

$direction : string = 'ASC'

Sort direction: 'ASC' or 'DESC' (default: 'ASC')

Tags
since
1.0.0
example

Single Column Sorting

$users = (new User())
    ->orderBy('name', 'ASC')
    ->getAsModels();
example

Multiple Column Sorting

$users = (new User())
    ->orderBy('priority', 'DESC')
    ->orderBy('created_at', 'ASC')
    ->getAsModels();
see
orderByRaw()

For raw SQL ordering

Return values
static

Current model instance for method chaining

orderByRaw()

Add raw SQL ORDER BY clause to query

public orderByRaw(string $clause) : static

Allows complex ordering with raw SQL expressions, functions, and calculations. Provides maximum flexibility for advanced sorting requirements beyond simple column ordering.

Parameters
$clause : string

Raw SQL ORDER BY clause (without ORDER BY keyword)

Tags
since
1.0.0
example

Function-Based Ordering

$users = (new User())
    ->orderByRaw('RAND()')  // Random order
    ->limit(10)
    ->getAsModels();
example

Complex Expressions

$users = (new User())
    ->orderByRaw('CASE WHEN priority IS NULL THEN 1 ELSE 0 END, priority DESC')
    ->getAsModels();
see
orderBy()

For standard column ordering

Return values
static

Current model instance for method chaining

orWhere()

Add OR WHERE condition to query builder

public orWhere(mixed ...$args) : static

Adds OR condition to existing WHERE clause. Requires existing query builder state with previous conditions to combine with OR logic. Maintains query state for continued method chaining.

Parameters
$args : mixed

Variable arguments for OR WHERE condition

Tags
since
1.0.0
example

OR Conditions

$users = (new User())
    ->where('role', 'admin')
    ->orWhere('role', 'manager')
    ->orWhere('special_access', 1)
    ->getAsModels();
example

Complex Logic

// (active = 1 AND role = 'user') OR (role = 'admin')
$users = (new User())
    ->where('active', 1)
    ->where('role', 'user')
    ->orWhere('role', 'admin')
    ->getAsModels();
see
QueryBuilder::orWhere()

For underlying implementation

Return values
static

Current model instance for method chaining

paginate()

Execute query with pagination and return raw data results

public paginate([int $perPage = 15 ][, int $page = 1 ]) : Paginator

Performs paginated query execution returning raw associative arrays. Provides pagination metadata through Paginator object including total count, page information, and navigation helpers.

Parameters
$perPage : int = 15

Number of records per page

$page : int = 1

Current page number (1-based)

Tags
since
1.0.0
example

Basic Pagination

$paginator = (new User())
    ->where('active', 1)
    ->paginate(20, 2); // 20 per page, page 2

$users = $paginator->getData(); // Raw arrays
$total = $paginator->getTotal();
$hasMore = $paginator->hasMorePages();
see
paginateAsModels()

For model object results

see
Paginator

For pagination functionality

Return values
Paginator

Pagination results with raw data arrays

paginateAsModels()

Execute query with pagination and return model instance results

public paginateAsModels([int $perPage = 15 ][, int $page = 1 ]) : Paginator

Performs paginated query execution and converts each result row into a model instance. Combines pagination functionality with object-oriented result access for comprehensive data handling.

Parameters
$perPage : int = 15

Number of records per page

$page : int = 1

Current page number (1-based)

Tags
since
1.0.0
example

Model Instance Pagination

$paginator = (new User())
    ->where('active', 1)
    ->paginateAsModels(15, 1);

foreach ($paginator->getData() as $user) {
    echo $user->name; // Model object access
    $user->updateLastSeen(); // Model methods available
}
see
paginate()

For raw data results

see
findPageAsModels()

Alias method

Return values
Paginator

Pagination results with model instances

prevPageUrl()

Generate URL for previous page in pagination sequence

public prevPageUrl(int $perPage, int $currentPage, string $baseUrl[, array<string, mixed> $extraParams = [] ]) : string|null

Creates properly formatted URL for the previous page with pagination parameters and optional additional query parameters. Returns null if already on first page.

Parameters
$perPage : int

Items per page for URL parameters

$currentPage : int

Current page number for calculation

$baseUrl : string

Base URL without query parameters

$extraParams : array<string, mixed> = []

Additional query parameters to include

Tags
since
1.0.0
example

Navigation URL Generation

$prevUrl = $user->prevPageUrl(20, 3, '/users', ['status' => 'active']);
// Result: '/users?page=2&limit=20&status=active'
Return values
string|null

Previous page URL or null if on first page

query()

Create new QueryBuilder instance with automatic soft delete filtering

public query() : QueryBuilder

Initializes fresh QueryBuilder for the model's table with automatic soft delete exclusion if enabled. Provides foundation for all query operations with consistent filtering behavior.

Automatic Filtering

  • Applies 'deleted_at IS NULL' condition if soft deletes enabled
  • Respects $includeSoftDeleted flag from withTrashed() method
  • Provides clean base for additional query conditions
Tags
since
1.0.0
example

Manual Query Building

$qb = (new User())->query()
    ->select(['name', 'email'])
    ->where('active', 1)
    ->orderBy('created_at', 'DESC');

$results = $qb->get($db);
see
QueryBuilder

For query building functionality

Return values
QueryBuilder

Fresh query builder instance for this model

reloadColumns()

Force reload of table column information from database

public reloadColumns() : void

Performs fresh DESCRIBE query on the model's table and updates the Cargo cache with current column information. Useful after schema changes or when cache becomes stale.

Cache Management

  • Clears existing cached columns for this table
  • Executes DESCRIBE query with QueryTracker monitoring
  • Updates Cargo cache with fresh column list
  • Provides foundation for accurate introspection
Tags
throws
RuntimeException

If DESCRIBE query fails or table doesn't exist

since
1.0.0
example

Manual Cache Refresh

// After schema migration
$user = new User();
$user->reloadColumns(); // Fresh column discovery
see
Cargo

For caching system

see
QueryTracker

For query performance monitoring

resetQueryBuilder()

Clear current query builder state for fresh query building

public resetQueryBuilder() : static

Resets the query builder to null, allowing subsequent query methods to start with a clean slate. Useful when reusing model instances for multiple different queries.

Tags
since
1.0.0
example

Query State Reset

$userModel = new User();

// First query
$activeUsers = $userModel->where('active', 1)->getAsModels();

// Reset and build different query
$inactiveUsers = $userModel
    ->resetQueryBuilder()
    ->where('active', 0)
    ->getAsModels();
Return values
static

Current model instance for method chaining

restore()

Restore soft-deleted record by clearing deleted_at timestamp

public restore() : bool

Recovers previously soft-deleted record by setting deleted_at to null and recording restoration timestamp. Only works with soft delete enabled models that have been previously deleted.

Restoration Process

  • Sets deleted_at field to null
  • Sets restored_at timestamp to current datetime
  • Record becomes visible in normal queries again
  • Triggers save() operation to persist changes
Tags
since
1.0.0
example

Record Restoration

// Find and restore deleted user
$user = (new User())->withTrashed()->find(123);
if ($user && $user->restore()) {
    echo "User restored successfully";
}

// User now appears in normal queries
$restored = (new User())->find(123); // Returns user object
example

Bulk Restoration

$deletedUsers = (new User())->onlyTrashed();
foreach ($deletedUsers as $userData) {
    $user = (new User())->fill($userData);
    $user->restore();
}
see
delete()

For soft delete process

see
withTrashed()

For querying deleted records

Return values
bool

True if restoration successful, false if not soft delete model or no primary key

rules()

Define validation rules for model attributes

public rules() : array<string, string>

Override this method in child classes to specify validation rules for model attributes. Rules are used by isValid() method through the Validator system for data validation before save operations.

Tags
since
1.0.0
example

Validation Rules Definition

class User extends Model
{
    public function rules(): array
    {
        return [
            'email' => 'required|email|unique:users,email',
            'name' => 'required|min:2|max:100',
            'age' => 'integer|min:0|max:150',
            'password' => 'required|min:8'
        ];
    }
}
see
isValid()

For validation execution

see
Validator

For validation rule syntax

Return values
array<string, string>

Attribute validation rules

save()

Persist model changes to database with validation and timestamp management

public save() : bool

Validates model data, manages timestamps, and performs INSERT or UPDATE based on model state. Returns true on success, false on validation failure or database error.

Save Process

  1. Runs validation via isValid() method
  2. Updates timestamp fields if enabled
  3. Determines INSERT vs UPDATE based on exists()
  4. Executes appropriate database operation
  5. Updates model state on success

Timestamp Management

  • Sets updated_at on all save operations
  • Sets created_at only on new record creation
  • Uses current datetime in Y-m-d H:i:s format
Tags
since
1.0.0
example

New Record Creation

$user = new User(['name' => 'John', 'email' => 'john@example.com']);
if ($user->save()) {
    echo "User created with ID: " . $user->id;
} else {
    print_r($user->getErrors());
}
example

Updating Existing Record

$user = (new User())->find(123);
$user->name = 'Updated Name';
$user->save(); // Updates existing record
see
isValid()

For validation process

see
insertRow()

For new record creation

see
updateRow()

For existing record updates

Return values
bool

True if save successful, false if validation failed or database error

setContents()

Serialize and store session data array as JSON in contents field

public setContents(array<string, mixed> $contents) : void

Converts a PHP associative array into JSON format and stores it in the database contents field. Does not automatically save to database - call save() method separately to persist changes.

Serialization Process

  • Uses json_encode for consistent cross-platform serialization
  • Handles nested arrays, objects, and mixed data types
  • Stores result directly in contents property
  • Does not validate array structure or content

Data Requirements

  • Input must be serializable by json_encode
  • Avoid circular references in nested structures
  • Resource types and closures cannot be serialized
  • UTF-8 encoding required for string values
Parameters
$contents : array<string, mixed>

Associative array of session data to serialize

Tags
since
1.0.0
example
$safe = SafeModel::findBySafeId($sessionId);

// Update session data
$sessionData = $safe->getContents();
$sessionData['user_id'] = 123;
$sessionData['last_activity'] = time();

// Store updated data (remember to save!)
$safe->setContents($sessionData);
$safe->save(); // Don't forget this step!

toArray()

Convert model data to associative array

public toArray() : array<string, mixed>

Returns the complete model data array with all attribute values. Useful for serialization, API responses, and data transformation operations where array format is required.

Tags
since
1.0.0
example

Data Export

$user = (new User())->find(123);
$userData = $user->toArray();

file_put_contents('user.json', json_encode($userData));
example

API Response

$users = (new User())->getAsModels();
$response = array_map(fn($user) => $user->toArray(), $users);
echo json_encode($response);
see
asArray()

Alias method

see
jsonSerialize()

For JSON serialization

Return values
array<string, mixed>

All model attributes as associative array

where()

Add WHERE condition to query builder with flexible parameter support

public where(mixed ...$args) : static

Supports both standard where conditions and raw SQL strings. Initializes query builder if needed and preserves query state for method chaining. Handles multiple parameter formats for maximum flexibility.

Parameter Formats

  • Single string: Raw SQL condition
  • Multiple parameters: Standard where($column, $operator, $value) or where($column, $value)
Parameters
$args : mixed

Variable arguments for WHERE condition

Tags
since
1.0.0
example

Standard WHERE Conditions

$users = (new User())
    ->where('active', 1)
    ->where('age', '>=', 18)
    ->where('status', '!=', 'banned')
    ->getAsModels();
example

Raw SQL Conditions

$users = (new User())
    ->where('DATE(created_at) = CURDATE()')
    ->where('email LIKE "%@company.com"')
    ->getAsModels();
see
QueryBuilder::where()

For underlying implementation

see
andWhere()

For explicit AND conditions

see
orWhere()

For OR conditions

Return values
static

Current model instance for method chaining

withTrashed()

Include soft-deleted records in subsequent queries

public withTrashed() : static

Modifies the model state to include records with deleted_at timestamps in query results. Only affects the current model instance and resets after query execution.

Tags
since
1.0.0
example

Including Deleted Records

// Find all users including soft-deleted
$allUsers = (new User())->withTrashed()->findAll();

// Chain with other query methods
$trashedActive = (new User())
    ->withTrashed()
    ->where('active', 1)
    ->getAsModels();
see
onlyTrashed()

To get only soft-deleted records

Return values
static

Current model instance for method chaining

castAttribute()

Apply configured type casting to attribute values

protected castAttribute(string $field, mixed $value) : mixed

Converts attribute values based on the $casts configuration array. Provides consistent type handling across all attribute operations with fallback values for invalid input data.

Supported Cast Types

  • bool: Uses filter_var(FILTER_VALIDATE_BOOLEAN), returns 1/0 for database
  • int: Validates numeric input, returns 0 for non-numeric values
  • float: Validates numeric input, returns 0.0 for non-numeric values
  • datetime: Converts DateTime objects and strings to 'Y-m-d H:i:s' format

Type Safety

Invalid values are converted to safe defaults rather than causing errors:

  • Non-boolean values become 0 (false) for bool type
  • Non-numeric values become 0 for int type
  • Non-numeric values become 0.0 for float type
  • Invalid datetime strings use strtotime() for parsing
Parameters
$field : string

Attribute name to check for casting configuration

$value : mixed

Raw value to apply casting to

Tags
since
1.0.0
example

Casting Configuration

protected array $casts = [
    'active' => 'bool',      // '1' becomes 1, 'false' becomes 0
    'age' => 'int',          // '25' becomes 25, 'abc' becomes 0
    'price' => 'float',      // '19.99' becomes 19.99
    'birthday' => 'datetime' // '2023-01-01' becomes '2023-01-01 00:00:00'
];
Return values
mixed

Casted value or original value if no casting defined

insertRow()

Insert new record into database with column filtering and tracking

protected insertRow() : bool

Performs INSERT operation for new records with automatic column validation, query tracking, and primary key assignment. Filters data to only include columns that exist in the database table.

Insert Process

  1. Gets cached table columns from Cargo
  2. Filters model data to only valid columns
  3. Builds parameterized INSERT SQL
  4. Executes with QueryTracker monitoring
  5. Sets primary key from lastInsertId()
  6. Updates original state for change tracking
  7. Records database errors if operation fails
Tags
throws
RuntimeException

If column cache is unavailable

since
1.0.0
see
QueryTracker

For query performance monitoring

see
Cargo

For column caching

Return values
bool

True if insert successful, false on database error

table()

Get the table name for this model with automatic generation fallback

protected table() : string

Returns the configured table name or generates one from the class name. Generation rule: lowercase class name + 's' suffix. Provides consistent table naming across the application.

Tags
since
1.0.0
example

Table Name Resolution

class User extends Model } // table: 'users'
class BlogPost extends Model } // table: 'blogposts'

class User extends Model {
    protected string $table = 'custom_users'; // table: 'custom_users'
}
Return values
string

Database table name for this model

updateRow()

Update existing database record with only changed fields

protected updateRow() : bool

Performs UPDATE operation using only dirty (changed) fields for performance optimization. Uses change tracking to minimize database operations and avoid unnecessary updates.

Update Process

  1. Gets dirty fields via getDirty()
  2. Returns true immediately if no changes
  3. Builds UPDATE SQL with only changed fields
  4. Executes with WHERE primary key condition
  5. Executes with QueryTracker monitoring
  6. Updates original state on success
  7. Records database errors if operation fails
Tags
since
1.0.0
see
getDirty()

For change detection

see
QueryTracker

For query performance monitoring

Return values
bool

True if update successful or no changes needed, false on database error


        
On this page

Search results