MagratheaModel — ORM Base Model
File: src/MagratheaModel.php Namespace: Magrathea2 Type: Abstract Class Implements: iMagratheaModel
The base class for all database-backed domain models. Provides field mapping, automatic CRUD operations, property access via __get/__set, serialization, and relationship definitions.
Interface: iMagratheaModel
Every model must satisfy:
interface iMagratheaModel {
public function __construct($id);
public function Save();
public function Insert();
public function Update();
public function GetID();
}
Defining a Model
<?php
namespace App\Models;
use Magrathea2\MagratheaModel;
class Product extends MagratheaModel {
// Required: database table name
protected $dbTable = "products";
// Optional: primary key column (default: "id")
protected $dbPk = "id";
// Required: column definitions [column_name => type]
protected $dbValues = [
"id" => "int",
"name" => "string",
"description" => "text",
"price" => "float",
"stock" => "int",
"active" => "boolean",
"created_at" => "datetime",
];
// Optional: property aliases [alias => real_column]
protected $dbAlias = [
"title" => "name", // $product->title is the same as $product->name
];
// Optional: eager-loaded relations (loaded on construct)
protected $autoLoad = null;
}
Supported Field Types
| Type | PHP type | Notes |
|---|---|---|
int | int | Integer values |
boolean | bool | Stored as TINYINT(1) |
string | string | VARCHAR, CHAR, etc. |
text | string | TEXT columns |
float | float | DECIMAL, FLOAT, DOUBLE |
datetime | string | MySQL datetime format (Y-m-d H:i:s) |
date | string | MySQL date format (Y-m-d) — see note below |
uuid | string | Auto-generated UUIDv7 on Insert() if left unset — see note below |
date normalization: on Insert()/Update(), a date field's value is normalized to Y-m-d before binding. Accepted inputs: "YYYY-MM-DD", "YYYY-MM-DD HH:MM:SS", and ISO-8601 ("YYYY-MM-DDTHH:MM:SS.sssZ"). Anything else throws a MagratheaModelException. The date part is taken as written — no timezone conversion happens, so "1990-05-10T03:00:00.000Z" stores 1990-05-10 regardless of server timezone. That is the right behavior for birthdate-style fields, but clients east/west of UTC should prefer sending a plain "YYYY-MM-DD" to avoid off-by-one-day surprises.
uuid generation: on Insert()/InsertWithPk(), any field declared "uuid" in $dbValues that is still empty gets filled in with Uuid::V7() (src/Uuid.php) — a time-ordered RFC 9562 UUIDv7: a 48-bit millisecond timestamp prefix followed by 10 bytes from PHP's CSPRNG (random_bytes()), with the version/variant bits set per spec. A field that already has a value (set explicitly before Insert()) is left alone and never overwritten. Nothing regenerates it on Update() — UUIDs are treated as immutable once assigned.
Collision-safety is not enforced by the framework: there is no pre-insert uniqueness check and no catch/retry around a duplicate-key error on INSERT — a collision would surface as a rethrown DB exception. In practice this is safe because random_bytes(10) supplies enough entropy that a same-millisecond collision is statistically negligible, but the real backstop is the database: the uuid column must actually be declared PRIMARY KEY/UNIQUE in the schema for uniqueness to be guaranteed rather than merely "very likely."
Strict typing ($strictTypes)
By default, LoadObjectFromTableRow() assigns whatever the DB driver returns for each column with no type coercion — mysqli/PDO return every column as a string regardless of its SQL type, so numeric and boolean properties end up as strings unless the calling code casts them.
Set protected $strictTypes = true; on a model (or its Base class) to opt into typed hydration: values are cast to their declared $dbValues PHP type on load — int → (int), boolean → (bool), float → (float). null is always left as null, and string/text/uuid/date/datetime fields are left untouched (the DB driver already returns those as strings). A value that can't be cast — schema drift, e.g. a non-numeric string landing in an int column — throws MagratheaModelException naming the field and value.
$strictTypes defaults to false and must be opted into per model; it does not change any existing model's behavior. Requires MagratheaPHP2 2.3.0+.
Properties
| Property | Type | Description |
|---|---|---|
$dbTable | string | Database table name |
$dbPk | string | Primary key column name |
$dbValues | array | Column → type definitions |
$dbAlias | array | Alias → column mappings |
$relations | array | Related object definitions |
$dirtyValues | array | Modified-but-not-saved fields |
$autoLoad | array|null | Relations loaded in constructor |
$strictTypes | bool | Opt-in typed hydration on load — see Strict typing above (default false) |
Instantiation
// Empty model
$product = new Product();
// Load by primary key
$product = new Product(42);
// Equivalent to: SELECT * FROM products WHERE id = 42
Persistence Methods
Save(): int|bool
Smart save: calls Insert() if the model has no PK set, or Update() if it does.
// Create new record
$product = new Product();
$product->name = "Widget";
$product->price = 9.99;
$newId = $product->Save(); // returns inserted ID
// Update existing record
$product = new Product(42);
$product->price = 12.99;
$product->Save(); // returns true
Insert(): int
Executes an INSERT and sets the PK on the model. Returns the new auto-increment ID.
$product = new Product();
$product->name = "New Product";
$id = $product->Insert();
InsertWithPk(): bool
Inserts a record that already has a PK set (e.g., UUID or custom integer).
$product = new Product();
$product->id = 9999;
$product->name = "Special Product";
$product->InsertWithPk();
Update(): bool
Executes an UPDATE for the current model (uses PK in WHERE clause).
$product = new Product(42);
$product->name = "Updated Name";
$product->Update(); // UPDATE products SET name = 'Updated Name' WHERE id = 42
Delete(): bool
Deletes the record from the database.
$product = new Product(42);
$product->Delete(); // DELETE FROM products WHERE id = 42
Property Access
Models support both method-style and magic property access:
// Magic access (recommended)
echo $product->name;
$product->price = 19.99;
// Method access
echo $product->Get("name");
$product->Set("price", 19.99);
// Suppress missing-field exceptions
$val = $product->Get("nonexistent", true); // returns null instead of throwing
Setting the Primary Key
$product->SetPK(42);
echo $product->GetPK(); // 42
echo $product->GetID(); // same
Loading Methods
LoadObjectFromTableRow(array|object $row): void
Populates model properties from a database row (array or stdClass). Used internally by MagratheaModelControl. If $strictTypes is enabled, values are cast to their declared $dbValues type — see Strict typing above.
$row = Database::Instance()->QueryRow("SELECT * FROM products WHERE id = 1");
$product = new Product();
$product->LoadObjectFromTableRow($row);
Assign(array $data): MagratheaModel
Assign multiple properties from an associative array (e.g., from $_POST).
$product = new Product();
$product->Assign([
"name" => "Widget",
"price" => 9.99,
]);
$product->Save();
GetById(mixed $id): void|object
Load the model data from the database by primary key. Throws MagratheaModelException if not found.
$product = new Product();
$product->GetById(42);
Introspection Methods
GetDbTable(): string
Returns the table name.
GetPkName(): string
Returns the primary key column name.
GetDbValues(): array
Returns the column definitions array.
GetFields(): array
Returns the list of column names.
GetFieldsForSelect(): string
Returns a comma-separated SQL field list with table prefix.
GetProperties(): array
Returns the model's current field values as an associative array.
IsEmpty(): bool
Returns true if the PK is not set (model not loaded from DB).
ModelName(): string
Returns the class short name.
Ref(): string
Returns a human-readable reference string (e.g., "Product#42").
Serialization
ToArray(): array
Returns all field values as an associative array.
$arr = $product->ToArray();
// ["id" => 42, "name" => "Widget", "price" => 9.99, ...]
ToJson(): array
Same as ToArray() but intended for JSON API responses. Relations are recursively serialized.
echo json_encode($product->ToJson());
ToString(): string / __toString(): string
echo $product; // uses __toString
Static Methods
GetDataTypeFromField(string $field): string
Returns the PHP type string for a field name.
IncludeAllModels(): void
Manually includes all model files (if needed outside the autoloader).
Getting the Next Available ID
GetNextID(): int
Returns MAX(pk) + 1 for the table. Useful when you need to know the next ID before inserting.
$nextId = $product->GetNextID();
Full Lifecycle Example
use App\Models\Product;
// 1. Create
$product = new Product();
$product->name = "Gadget";
$product->price = 29.99;
$product->stock = 100;
$product->active = true;
$id = $product->Save();
echo "Created product #$id";
// 2. Read
$found = new Product($id);
echo $found->name; // "Gadget"
echo $found->price; // 29.99
// 3. Update
$found->price = 24.99;
$found->Save();
// 4. Serialize
echo json_encode($found->ToJson());
// 5. Delete
$found->Delete();
Notes
- The constructor accepts an optional
$id. If provided, it immediately queries the database. Save()is the idiomatic method — it detects insert vs. update automatically based on whether the PK is set.- Fields not in
$dbValuesare silently ignored during insert/update (they won't corrupt the DB). $dirtyValuestracks which fields changed since last load, but the current implementation updates all fields onUpdate().
Class Reference — MagratheaModel
Gets an array of whatever and assign it to the properties of model
| Param | Type | Default |
|---|---|---|
$data |
mixed | required |
Deletes the object in database
gets required property
| Param | Type | Default |
|---|---|---|
$key |
mixed | required |
$supressException |
mixed | false |
Gets autoload objects
Returns object by Id. If null, creates a null instance of the object. This will also load any related objects that are set as "autoload" internally. if an object with the given id can not be found, or any of the auto-load related objects can not be found an exception will be thrown.
| Param | Type | Default |
|---|---|---|
$id |
mixed | required |
Gets table related to model
Gets array of table column values
Get fields from model
Prepare fields for this model for a select statement
Gets id value
Gets the next auto increment id for this object
Gets PK Name
Get all properties from model
Inserts the object in database
Inserts the object, but the object alredy has the PK
Checks if the object exists (id not null)
Receives an array with the columns and values and associates then internally into the object
| Param | Type | Default |
|---|---|---|
$row |
mixed | required |
returns the name of the class without the namespace
returns a string for identifying the object in a relation
Saves: Using a insert if pk is not set and an update if pk is set Basically, Inserts if id does not exists and updates if id does exists
Sets given property
| Param | Type | Default |
|---|---|---|
$key |
mixed | required |
$value |
mixed | required |
$supressException |
mixed | false |
Sets PK
| Param | Type | Default |
|---|---|---|
$value |
mixed | required |
Gets a Json
To String! =)
Updates the object in database
Get (Magrathea) data type from field
| Param | Type | Default |
|---|---|---|
$field |
mixed | required |
Include all classes presents on `Models` folder