Overview
Architecture
WPZylos Model follows the Active Record pattern, where each model class maps to a database table and each instance represents a single row. The model combines data access and business logic in one object.
┌─────────────────────────────────────────────────┐
| Your Model |
| (extends Model abstract class) |
+--───────────────────────────────────────────────┤
| HasAttributes | Casting, accessors, dirty |
| HasTimestamps | created_at, updated_at |
| HasEvents | Lifecycle hooks, boot |
| HasRelationships | hasOne, hasMany, belongsTo |
| SoftDeletes (opt)| Soft delete support |
+--───────────────────────────────────────────────┤
| QueryBuilder |
| (from wpzylos-database) |
+--───────────────────────────────────────────────┤
| Connection |
| (wraps $wpdb) |
+--───────────────────────────────────────────────┘
Design Patterns
Active Record
Each model class knows how to persist itself:
$user = User::create(['name' => 'John']); // INSERT
$user->name = 'Jane';
$user->save(); // UPDATE
$user->delete(); // DELETE
Trait Composition
The base Model class is kept lean by extracting concerns into traits:
HasAttributes— Attribute storage, type casting, accessors/mutators, dirty trackingHasTimestamps— Auto-managedcreated_at/updated_atHasEvents— Static boot pattern, lifecycle event registration and firingHasRelationships—hasOne,hasMany,belongsTowith relation cachingSoftDeletes— Optional trait for soft-delete behavior
Service Provider Pattern
ModelServiceProvider registers into the WPZylos container and injects the ApplicationInterface into the base Model so it can resolve database connections and table names.
Factory / Resolver
ModelResolver serves as a central factory registered as a singleton, allowing the container to create or find model instances by class name.
How It Works
1. Model Registration
When your plugin boots, register the ModelServiceProvider:
$app->register(new ModelServiceProvider());
This:
- Registers
ModelResolveras a singleton in the container - Calls
Model::setApplication($app)so all models can access the container
2. Table Resolution
Each model declares its table name (without WP prefix):
protected static string $table = 'orders';
At runtime, Model::resolveTableName() uses the application context to prepend the WordPress table prefix (e.g., wp_orders).
3. Query Proxy
Static methods like Model::where(), Model::find(), and Model::all() create a QueryBuilder instance bound to the model's table. The QueryBuilder (from wpzylos-database) handles SQL generation and execution through $wpdb.
4. Hydration
When rows are fetched from the database, Model::newFromRow() creates a new model instance, fills its attributes from the stdClass row, syncs the original snapshot, and marks $exists = true.
5. Lifecycle Events
The static boot() method is called once per model class on first instantiation. Child models override it to register event listeners. Events like creating, updating, deleting can cancel operations by returning false.
Dependencies
| Package | Purpose |
|---|---|
wpzylos-core | Application container, ServiceProvider base, ApplicationInterface |
wpzylos-database | Connection (wraps $wpdb) and QueryBuilder |