phpEZ v0.1.2

Model extends Obj
in package

AbstractYes

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
see
Database
CachableModel
subpackage

db

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
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
tbl()  : string
Get the full table name including prefix.

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'
#[DoNotSerialize]
#[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'
#[DoNotSerialize]
#[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
#[DoNotDeserialize]
#[DoNotSerialize]

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:

  1. Skips static properties and those with #[DoNotSerialize]
  2. Skips uninitialized properties with #[OmitEmpty]
  3. For Parsable fields: calls marshall()
  4. For objects with __serialize(): calls __serialize()
  5. For base types (string, int, bool, float, null): includes as-is

Returns a flat array with property names as keys.

Tags
throws
HTTPException

For unsupported field types or serialization errors.

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:

  1. Skips static properties and those with #[DoNotDeserialize]
  2. Validates mandatory fields (non-nullable types with no value)
  3. For #[OmitEmpty] fields: allows missing data
  4. For Parsable types: calls parse()
  5. For objects with __unserialize(): recursively deserializes
  6. 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
throws
HTTPException

For validation errors (400) or unsupported types (500).

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
throws
DataException

For constraint creation failures.

example
User::createTable();
Post::createTable();
Post::createDeps();  // Links posts to users

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
throws
DataException

For table creation failures.

example
User::createTable();        // Creates table if not exists
User::createTable(true);    // Drops and recreates table

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
throws
DataException

For unsupported field types.

example
echo User::ddl();
// CREATE TABLE IF NOT EXISTS app_User (
//   `id` INT AUTO_INCREMENT PRIMARY KEY,
//   `email` VARCHAR(255) UNIQUE NOT NULL,
//   `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
//   ...
// )
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
example
class Post extends Model {
  #[Foreign(User::class, DbThen::CASCADE, DbThen::CASCADE)]
  public int $user_id;
}

$ddl = Post::ddlDeps();
// Returns: [
//   "ALTER TABLE app_Post
//    ADD CONSTRAINT app_Post_ibfk_abc123
//    FOREIGN KEY (user_id) REFERENCES app_User(id)
//    ON DELETE CASCADE ON UPDATE CASCADE"
// ]
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
throws
DataException

If model has no ID or deletion fails.

example
$user = User::find(42);
$user->delete();  // Deletes the row

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
throws
DataException

If multiple records match (field should be unique).

example
$user = User::find('alice@example.com', 'email');
$post = Post::find(42);  // Implicitly searches by 'id'
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
throws
DataException

For query execution failures.

example
$admins = User::findMany('role = :role AND active = :active',
  ['role' => 'admin', 'active' => 1]);
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
throws
DataException

If model not found in session.

example
$user = User::find(42);
$user->toSex('current');

// Later, in another request:
$user = User::fromSex('current');
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:

  1. Calls beforeSave() hook
  2. Serializes model to get column values
  3. Generates and executes INSERT or UPDATE query
  4. For new records: sets ID from lastInsertId()
  5. 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
throws
DataException

For operation failures or validation errors.

DuplicateException

For unique constraint violations (HTTP 400).

example
$user = new User(['name' => 'Alice']);
$user->save(forCreate: true);  // INSERT, sets $user->id

$user->name = 'Alicia';
$user->save();  // UPDATE using existing ID
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

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
throws
HTTPException

If model is not persisted or has unsaved changes.

example
$user = User::find(42);
$user->toSex();  // Stored in $_SESSION['User']

// Retrieve from another request:
$user = User::fromSex();
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
throws
HTTPException

(400) For invalid JSON or deserialization errors.

example
$request = new CreateUserRequest();
$request->unserializeRaw(file_get_contents('php://input'));
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
Return values
string

tbl()

Get the full table name including prefix.

protected static tbl() : string
Return values
string

The prefixed table name derived from class name.

On this page

Search results