Application Models
Application Models
Hazaar provides a powerful data modeling system to help sanitize, validate, and structure your application's data. While standard PHP classes are great for logic, Hazaar Models are designed specifically for handling data structures, serving as smart containers or Data Transfer Objects (DTOs) with built-in utility.
For full technical details, please refer to the Hazaar\Model\Struct API Documentation.
Why use Hazaar Models?
You might wonder why you should extend Hazaar\Model\Struct or Hazaar\Model\Schema instead of just using a standard stdClass or a plain PHP class. The Hazaar Model system offers several distinct advantages that streamline development:
- Automatic Population (Mass Assignment): Models can be instantly populated from arrays (like
$_POSTor database rows) or other objects. The model matches keys to property names, so you don't have to manually assign every field. - Type Safety & Coercion: When data is assigned to a model property, Hazaar automatically attempts to cast the value to the correct type defined by the property. For example, if you assign the string
"123"to a property typed asint, it will be converted to an integer automatically. - Built-in Serialization: Converting objects to Arrays or JSON is a common requirement for APIs. Hazaar models come with
toArray()andtoJSON()methods out of the box. You can also control exactly which properties are exposed using attributes like#[Hide]. - Validation: You can define rules using attributes (e.g.,
#[Required],#[Filter(FILTER_VALIDATE_EMAIL)]) on properties in any model. This ensures that data integrity is maintained automatically as values are assigned. - Event Hooks: Models support internal hooks that allow you to modify data seamlessly as it is read or written. For PHP 8.4+ applications, using native Property Hooks is recommended for better performance.
Types of Models
Hazaar offers two primary base classes for modeling data:
Hazaar\Model\Struct
This is the general-purpose base class for data models. It provides type safety, automatic casting, and serialization helpers.
Important: In a Struct, validation is strict. If a validation rule (attribute) fails during assignment, a Hazaar\Exception\Validation\Error is thrown immediately, halting execution. This is best for internal data integrity or API responses where bad data is exceptional.
Hazaar\Model\Schema
This abstract class extends Struct and is designed for validating complex user input (like forms). Instead of throwing exceptions immediately, Schema collects validation errors internally.
You can populate a Schema with potentially invalid data, then call methods like $model->validate() to check for validity and $model->getValidationErrors() to retrieve the list of errors for display.
Defining a Model
To create a model, define a class that extends Hazaar\Model\Struct or Hazaar\Model\Schema.
Public and protected properties in your class define the structure of the data.
Important: If you intend to use Validation Attributes (like #[Required]), the properties must be protected. This forces PHP to use the internal __set magic method where validation logic resides. Public properties allow direct modification, bypassing validation.
<?php
use Hazaar\Model\Struct;
use Hazaar\Model\Attribute\Required;
class User extends Struct {
protected int $id;
#[Required]
protected string $username;
protected string $email;
protected ?string $bio = null; // Nullable property
}Lifecycle Methods
Models provide protected placeholder methods that you can override to hook into the object's lifecycle without needing to manually call parent constructors.
construct(array &$data): Called before properties are populated. You can modify$datahere.constructed(): Called after properties are populated and initialization is complete.destruct(): Called when the object is destroyed.
protected function construct(array &$data): void {
if (isset($data['full_name'])) {
$parts = explode(' ', $data['full_name']);
$data['first_name'] = $parts[0];
}
}Using a Model
You can instantiate a model with an array or object of data. The model will automatically populate its properties from the provided data.
$data = [
'id' => 123,
'username' => 'jdoe',
'email' => '[email protected]'
];
$user = new User($data);
echo $user->username; // Accessing protected property via __get magic methodData Features
Type Casting & Nested Models
Hazaar provides sophisticated type coercion when assigning values to properties.
- Simple Types: Assigning
"123"to anintproperty converts it to integer123. - DateTime: Assigning a string timestamp (e.g.,
"2023-01-01") or an integer unix timestamp to aDateTimetyped property automatically creates aDateTimeobject. - Nested Models: If a property is typed as another Model class (e.g.,
public Address $address;), assigning an array of data to it will automatically instantiate and populate a newAddressmodel.
Typed Arrays: You can use PHPDoc @var annotations to define arrays of specific types. Hazaar will automatically cast each item in the input array to the specified type.
class User extends Struct {
/**
* @var array<Post>
*/
public array $posts;
}
// Input data containing an array of raw post data
$data = ['posts' => [['title' => 'First!'], ['title' => 'Second!']]];
$user = new User($data);
// $user->posts is now an array of Post objects
echo $user->posts[0]->title;Dynamic Properties
While defining properties in the class is the standard way to use models, Hazaar\Model\Struct also supports dynamic properties at runtime. This is useful for flexible data structures where keys are not known in advance.
You can define a property dynamically using defineProperty:
$model->defineProperty('string', 'customField', 'default value');
$model->customField = "Dynamic Value";Array Access & Iteration
Models implement the ArrayAccess, Iterator, and Countable interfaces.
- Iteration: You can loop over the model properties like an array. Hidden properties are skipped.
- Array Access: You can access properties using array syntax (
$model['key']), which maps internally to__getand__set. - Countable:
count($model)returns the number of defined properties.
// Array access
$user['username'] = 'new_name';
// Iteration
foreach($user as $key => $value) {
echo "$key: $value\n";
}
// Counting
echo count($user); // Returns number of propertiesMethods
has(string $key): bool
Checks if a property exists in the model (either defined in the class or dynamically added).
if ($user->has('email')) {
// ...
}keys(): array
Returns a list of all property names in the model.
print_r($user->keys());populate($data)
Populates the model with data from an array or object. This method performs a "strict sync": it iterates over the model's defined properties, looking for matching keys in the input data.
- If a property exists in the model but is missing from the input
$data, it may be reset tonullor its default value (depending on implementation details). - Keys in
$datathat do not correspond to any property in the model are ignored.
$user->populate(['username' => 'newname']);extend($data)
Extends the model with additional data. Unlike populate, this method iterates over the input data.
- It only updates properties that are present in the input
$data. - Existing properties in the model that are NOT in the input
$dataare left untouched. - This is ideal for partial updates (e.g., HTTP PATCH requests).
// Only updates 'bio', leaves 'username' and 'email' as is
$user->extend(['bio' => 'Updated bio']);toArray($context = null)
Converts the model and its properties into an associative array.
You can optionally pass a context string. If provided, any properties marked with the #[Hide('context_name')] attribute matching that context will be excluded from the resulting array.
// Standard conversion
$array = $user->toArray();
// Conversion with a specific context (hides properties marked with #[Hide('public')])
$publicData = $user->toArray('public');toJSON($context = null)
Converts the model directly to a JSON string. Like toArray(), it accepts a context string to filter properties.
echo $user->toJSON('api');Because Hazaar Models implement the \JsonSerializable interface, you can also simply pass the model instance to json_encode().
echo json_encode($user); // Same as $user->toJSON()Note: When using
json_encode(), thejsonSerialize()method is called internally which does not accept a context. If you need to hide properties based on context, you must use$model->toJSON('context')explicitly.
Event Hooks
While PHP Property Hooks handle property-level logic, the Hazaar Model system provides Object Event Hooks for lifecycle events affecting the entire model. These hooks are unique to the framework and are not replaced by native property hooks (though property hooks on individual properties will still fire when these operations access them).
You can define these hooks using defineEventHook($hookName, $callback).
Available Object Hooks
populate: Triggered before data is loaded into the model viapopulate().populated: Triggered after data has been loaded.extend: Triggered before the model is extended with new data usingextend().extended: Triggered after the model has been extended.serialize: Triggered before the model is converted to an array (e.g., viatoArray()).serialized: Triggered after the model has been converted to an array.json: Triggered before the model is converted to JSON.
$user = new User();
// Define a hook to log when data is populated
$user->defineEventHook('populated', function($data) {
error_log("User model populated with data!");
});
$user->populate(['id' => 1, 'username' => 'admin']);Property Hooks (PHP 8.4+)
For modern applications running on PHP 8.4 or newer, it is strongly recommended to use standard PHP Property Hooks for custom logic on get/set operations. This reduces overhead compared to the framework's internal event system.
class Product extends Struct {
public float $price {
set(float $value) => $value < 0 ? 0 : $value;
}
}For older PHP versions, defineEventHook() is still supported but considered legacy.
$model->defineEventHook('read', 'name', function($value){
return strtoupper($value);
});Validation and Attributes
The preferred way to apply validation rules and data transformations is using PHP Attributes. The defineRule() method exists for backward compatibility but using Attributes is cleaner and more performant.
Attributes are found in the Hazaar\Model\Attribute namespace.
Common Attributes
#[Required]
Ensures a value is not empty.
#[Required]
protected string $name;#[Min(int $value)]
Ensures an integer is greater than or equal to the specified value.
#[Min(18)]
protected int $age;#[Max(int $value)]
Ensures an integer is less than or equal to the specified value.
#[Max(100)]
protected int $percent;#[Range(int $min, int $max)]
Ensures an integer is within a specific range.
#[Range(1, 5)]
protected int $rating;#[MinLength(int $length)]
Ensures a string has a minimum character length.
#[MinLength(8)]
protected string $password;#[MaxLength(int $length)]
Ensures a string does not exceed a maximum character length.
#[MaxLength(255)]
protected string $description;#[Filter(int $filter, mixed $options)]
Applies a standard PHP filter_var validation or sanitization.
#[Filter(FILTER_VALIDATE_EMAIL)]
protected string $email;#[Trim(string $chars)]
Trims whitespace or specific characters from the string value.
#[Trim]
protected string $username;#[Pad(int $length)]
Pads a string to a specific length.
#[Pad(10)]
protected string $code;#[Truncate(int $length)]
Truncates a string to a maximum length.
#[Truncate(100)]
protected string $summary;#[Contains(string $needle)]
Ensures a string or array contains the specified value.
#[Contains("@")]
protected string $email;#[Hide(string $context)]
Prevents a property from being output when converting to array or JSON in a specific context.
#[Hide('public')]
protected string $secret;API Reference
Hazaar\Model\Struct- The base class for all models.Hazaar\Model\Schema- Extended model with validation collection.