Model
extends Obj
in package
Base ORM model class for database-backed entities.
Extends Obj with persistence layer: DDL generation, CRUD operations, and automatic timestamp tracking. Each Model subclass corresponds to a database table with automatic schema generation.
Key features:
- Automatic timestamps: created_at and updated_at fields auto-managed
- CRUD methods: find(), findMany(), save(), delete()
- DDL generation: Reflect properties to generate CREATE TABLE statements
- Foreign keys: Support for foreign key constraints with cascade behavior
- Dirty tracking: Detect if model has been modified since load
- Session persistence: Store/retrieve models from session (Sex)
- Type-mapped columns: Auto-detect SQL types from PHP property types
Naming conventions:
- Table name = Database prefix + Model class name
- Primary key = 'id' (auto-increment INT)
- Timestamps = created_at, updated_at (auto-managed)
Example:
class User extends Model {
public string $email;
#[Unique]
public string $username;
#[DoNotSerialize]
public string $password_hash;
}
// Create table
User::createTable();
// Create and persist
$user = new User(['email' => 'test@example.com', 'username' => 'alice']);
$user->save(forCreate: true); // Now has ID
// Find and update
$user = User::find('alice', 'username');
$user->email = 'newemail@example.com';
$user->save(); // Updates existing row
// Query multiple
$admins = User::findMany('role = :role', ['role' => 'admin']);
Tags
Table of Contents
Constants
- baseTypes : array<string|int, string> = ['string', 'int', 'bool', 'float', 'null']
- Base types that don't require special serialization handling.
Properties
- $created_at : DBDateTime|null
- Timestamp when the record was created.
- $updated_at : DBDateTime|null
- Timestamp when the record was last modified.
- $id : int|null
- Primary key of this model instance.
- $_hash : string|null
- Internal hash of serialized state for dirty tracking.
Methods
- __construct() : mixed
- Initialize a Model instance.
- __serialize() : array<string, mixed>
- Serialize this object to an array suitable for JSON encoding.
- __unserialize() : void
- Deserialize data into this object's properties.
- beforeSave() : mixed
- Lifecycle hook called before saving to the database.
- createDeps() : void
- Create foreign key constraints for this model.
- createTable() : void
- Create the database table for this model.
- ddl() : string
- Generate the CREATE TABLE DDL statement.
- ddlDeps() : array<string|int, string>
- Generate foreign key constraint ALTER statements.
- delete() : void
- Delete this model from the database.
- find() : static|null
- Find a single model instance by field value.
- findMany() : array<string|int, static>
- Find multiple model instances matching a condition.
- fromSex() : static
- Retrieve a model instance from the session (Sex).
- id() : int|null
- Get the primary key of this model.
- isDirty() : bool
- Check if this model has been modified since it was loaded/saved.
- jsonSerialize() : mixed
- Implement JsonSerializable to enable json_encode() on this object.
- save() : static
- Persist this model to the database as INSERT or UPDATE.
- serializeVal() : mixed
- tbl() : string
- Get the full table name including prefix.
- toSex() : static
- Store this model instance in the session (Sex).
- unserializeRaw() : void
- Deserialize a JSON string into this object.
- makeHash() : string
- Generate a SHA256 hash of the current serialized state.
- processTabTpls() : string
Constants
baseTypes
Base types that don't require special serialization handling.
protected
array<string|int, string>
baseTypes
= ['string', 'int', 'bool', 'float', 'null']
Properties
$created_at
Timestamp when the record was created.
public protected(set)
DBDateTime|null
$created_at
= \null
Auto-set by database to CURRENT_TIMESTAMP on insert. Not included in serialization (internal database field).
Attributes
- #[DbDefault]
- 'CURRENT_TIMESTAMP'
- #[NotNull]
$updated_at
Timestamp when the record was last modified.
public protected(set)
DBDateTime|null
$updated_at
= \null
Auto-updated by database to CURRENT_TIMESTAMP on any row modification. Not included in serialization (internal database field).
Attributes
- #[DbDefault]
- 'CURRENT_TIMESTAMP'
- #[NotNull]
- #[OnUpdate]
- 'CURRENT_TIMESTAMP'
$id
Primary key of this model instance.
protected
int|null
$id
= \null
Null for unsaved (new) instances. Auto-set by database on insert.
$_hash
Internal hash of serialized state for dirty tracking.
private
string|null
$_hash
= \null
Stored after load/save to detect modifications.
Attributes
Methods
__construct()
Initialize a Model instance.
public
__construct([mixed $data = null ]) : mixed
Parameters
- $data : mixed = null
-
Optional data to deserialize. If provided, resets the hash for dirty tracking.
__serialize()
Serialize this object to an array suitable for JSON encoding.
public
__serialize() : array<string, mixed>
Reflects on all properties and processes each according to:
- Skips static properties and those with #[DoNotSerialize]
- Skips uninitialized properties with #[OmitEmpty]
- For Parsable fields: calls marshall()
- For objects with __serialize(): calls __serialize()
- For base types (string, int, bool, float, null): includes as-is
Returns a flat array with property names as keys.
Tags
Return values
array<string, mixed> —Serialized representation suitable for json_encode().
__unserialize()
Deserialize data into this object's properties.
public
__unserialize(mixed $data) : void
Reflects on all properties and populates them from the input data array:
- Skips static properties and those with #[DoNotDeserialize]
- Validates mandatory fields (non-nullable types with no value)
- For #[OmitEmpty] fields: allows missing data
- For Parsable types: calls parse()
- For objects with __unserialize(): recursively deserializes
- For base types: assigns directly (with type coercion)
Enforces type safety: throws HTTPException (400) for validation failures, HTTPException (500) for unsupported types.
Parameters
- $data : mixed
-
Array-like data to deserialize (typically from json_decode(array)).
Tags
Return values
void —Properties are populated in-place.
beforeSave()
Lifecycle hook called before saving to the database.
public
beforeSave() : mixed
Override in subclasses to implement custom validation, normalization, or calculated fields before persistence.
Normally does nothing; can be customized for advanced logic.
Called by save() before INSERT or UPDATE.
createDeps()
Create foreign key constraints for this model.
public
static createDeps([bool $force = false ]) : void
Executes ALTER TABLE statements to establish foreign key relationships defined by #[Foreign] attributes. Optionally drops existing constraints first.
Must be called after createTable() if dependencies don't exist yet.
Parameters
- $force : bool = false
-
If true, drops constraints first.
Tags
createTable()
Create the database table for this model.
public
static createTable([bool $force = false ]) : void
Executes the DDL statement to create the table. Optionally drops the table first if it exists (force mode).
Parameters
- $force : bool = false
-
If true, drops table first (useful for schema resets).
Tags
ddl()
Generate the CREATE TABLE DDL statement.
public
static ddl([bool $force = false ]) : string
Reflects on model properties and generates SQL to create the corresponding table. Automatically handles:
- Property type mapping to SQL types (int → INT, string → VARCHAR, etc)
- Attributes: #[Unique], #[Index], #[NotNull], #[DbDefault], #[CustomType]
- Primary key (auto-increment id field)
- Composite indexes
- ON UPDATE actions (e.g., timestamp auto-update)
Skips:
- Static properties
- Private properties (starting with _)
- Properties with hooks (PHP 8.4 hooks)
Parameters
- $force : bool = false
-
If true, includes DELETE before CREATE (no IF NOT EXISTS). If false, uses CREATE TABLE IF NOT EXISTS.
Tags
Return values
string —The complete CREATE TABLE statement.
ddlDeps()
Generate foreign key constraint ALTER statements.
public
static ddlDeps([bool $force = false ]) : array<string|int, string>
Finds all properties with #[Foreign] attributes and generates ALTER TABLE statements to create the constraints.
Each foreign key:
- References the id column of the target Model's table
- Specifies ON DELETE and ON UPDATE actions
- Uses a hashed name to avoid conflicts
Parameters
- $force : bool = false
-
If true, includes DROP FOREIGN KEY statements first.
Tags
Return values
array<string|int, string> —Array of ALTER TABLE statements.
delete()
Delete this model from the database.
public
delete() : void
Removes the row with matching ID. Fails if model is not persisted (no ID).
Tags
find()
Find a single model instance by field value.
public
static find(string $id_or_val[, string $field = 'id' ]) : static|null
Queries for a specific value in a field and returns the first match. Throws DataException if multiple matches found (expected unique).
Parameters
- $id_or_val : string
-
The value to search for.
- $field : string = 'id'
-
The field name to query on. Defaults to 'id'.
Tags
Return values
static|null —The model instance if found, null if no match.
findMany()
Find multiple model instances matching a condition.
public
static findMany([string $cond = '1=1' ][, array<string|int, mixed> $fieldSet = [] ][, string $joins = '' ]) : array<string|int, static>
Executes a SELECT query with optional WHERE conditions and parameter binding. Uses prepared statements to prevent SQL injection.
Parameters
- $cond : string = '1=1'
-
SQL WHERE clause condition. Defaults to '1=1' (all rows). Use parameter placeholders like ':fieldname' for binding.
- $fieldSet : array<string|int, mixed> = []
-
Named parameters to bind to the query. Keys must match placeholders in $cond.
- $joins : string = ''
Tags
Return values
array<string|int, static> —Array of model instances, empty if no matches.
fromSex()
Retrieve a model instance from the session (Sex).
public
static fromSex([string|null $key = null ]) : static
Restores a model previously stored via toSex() from $_SESSION.
Parameters
- $key : string|null = null
-
Optional suffix for session key (must match toSex() key).
Tags
Return values
static —The restored model instance.
id()
Get the primary key of this model.
public
id() : int|null
Return values
int|null —The ID if persisted, null for new instances.
isDirty()
Check if this model has been modified since it was loaded/saved.
public
isDirty() : bool
Compares the current state hash with the hash saved at load time. Returns false for new unsaved instances (no baseline hash).
Return values
bool —True if model has changes, false if unchanged or new.
jsonSerialize()
Implement JsonSerializable to enable json_encode() on this object.
public
jsonSerialize() : mixed
Delegates to __serialize() so that json_encode($obj) works seamlessly.
Return values
mixed —The serialized array from __serialize().
save()
Persist this model to the database as INSERT or UPDATE.
public
save([bool $forCreate = false ][, int|null $idForUpdate = null ]) : static
Intelligently chooses INSERT for new instances (no ID) or UPDATE for existing ones (has ID). Handles duplicate key errors gracefully.
Flow:
- Calls beforeSave() hook
- Serializes model to get column values
- Generates and executes INSERT or UPDATE query
- For new records: sets ID from lastInsertId()
- Updates hash for dirty tracking
Parameters
- $forCreate : bool = false
-
Flag to enforce INSERT-only (fails if ID exists).
- $idForUpdate : int|null = null
-
ID to use as WHERE clause for UPDATE. Must match $this->id if both provided. Fails if provided without existing $this->id.
Tags
Return values
static —Returns $this for fluent interface.
serializeVal()
public
static serializeVal(mixed $val[, string|null $type = null ]) : mixed
Parameters
- $val : mixed
- $type : string|null = null
tbl()
Get the full table name including prefix.
public
static tbl() : string
Return values
string —The prefixed table name derived from class name.
toSex()
Store this model instance in the session (Sex).
public
toSex([string|null $key = null ]) : static
Persists the model to $_SESSION for retrieval across requests. Model must be persisted to database first (have an ID and no pending changes).
Uses the model's class name as the session key (optionally suffixed with $key).
Parameters
- $key : string|null = null
-
Optional suffix for session key (useful for storing multiple instances of the same model class).
Tags
Return values
static —Returns $this for fluent interface.
unserializeRaw()
Deserialize a JSON string into this object.
public
unserializeRaw(string $rawData) : void
Convenience method that decodes raw JSON and calls __unserialize(). Used by the API framework to populate request body objects.
Parameters
- $rawData : string
-
Raw JSON string from request body.
Tags
Return values
void —Properties are populated in-place.
makeHash()
Generate a SHA256 hash of the current serialized state.
protected
makeHash() : string
Used internally for dirty tracking: compare the hash at load time with the hash after modifications to detect changes.
Return values
string —SHA256 hash of sorted JSON representation.
processTabTpls()
protected
static processTabTpls(string $joins) : string
Parameters
- $joins : string