| Home | Getting Started | Core Concepts | Helpers | Extensions | Repo |
Models in Tiny encapsulate data access and business rules. They extend TinyModel, live in app/models/, and are loaded with tiny::model('name').
app/models/user.php:
<?php
class UserModel extends TinyModel
{
public function all(): array
{
return tiny::db()->getAll('users', '*', 'name ASC');
}
public function byId(int $id): ?array
{
return tiny::db()->getOne('users', ['id' => $id]) ?: null;
}
public function create(array $data): int|bool
{
return tiny::db()->insert('users', [
'name' => $data['name'],
'email' => $data['email'],
'created_at' => date('Y-m-d H:i:s'),
]);
}
public function update(int $id, array $data): bool
{
return (bool) tiny::db()->update('users', $data, ['id' => $id]);
}
public function delete(int $id): bool
{
return (bool) tiny::db()->delete('users', ['id' => $id]);
}
}
Naming convention: the file is
app/models/user.php, the class isUserModel.tiny::model('user')looks forapp/models/user.phpand instantiates the class found there.
class Users extends TinyController
{
private UserModel $model;
public function __construct()
{
parent::__construct();
$this->model = tiny::model('user');
}
public function get($request, $response)
{
$response->render('users/index', ['users' => $this->model->all()]);
}
}
Models are cached per request, so calling tiny::model('user') twice returns the same instance.
TinyModel ships with a schema validator. Define schemas as associative arrays and call isValid($data, $schema):
<?php
class UserModel extends TinyModel
{
public array $schemas = [
'account' => [
'name' => 'string(100)',
'email' => 'string(255)',
'active' => 'bool',
'role' => 'string',
'tags' => '[array(50)]', // optional, max 50 items
'bio' => '[string]|null', // either string or null (optional)
],
];
public function updateAccount(array $data): bool
{
if (!$this->isValid($data, $this->schemas['account'])) {
return false; // $this->validationErrors is now populated
}
return (bool) tiny::db()->update('users', $data, ['id' => $data['id']]);
}
}
| Token | Meaning |
|---|---|
string |
Must be a string |
int |
Must be an integer |
bool |
Must be a boolean |
float / double |
Must be a float |
array |
Must be an array |
object |
Must be an object |
callable / resource |
PHP standard |
datetime |
Parseable by strtotime |
string(255) |
String, max 255 chars |
array(10) |
Array, max 10 items |
[string] |
Optional (allows null / missing) |
string\|int |
Union — must satisfy any side |
MyEnum |
PHP enum case value |
MyClass |
Instance of class |
After validation, errors are available as $model->validationErrors (field → reason map). Use validationErrorsToAlpineJs() to project them into an Alpine.js binding string:
<div x-data="{ invalid: {} }" x-init="<?= $model->validationErrorsToAlpineJs() ?>">
<input :class="{ 'border-red-500': invalid.email }">
</div>
All DB helpers (see Database) are available via tiny::db():
public function activeUsers(int $limit = 50): array
{
return tiny::db()->get(
'users',
['status' => 'active'],
'id, name, email, last_login',
'last_login DESC',
$limit
);
}
public function recentSignups(string $since): array
{
return tiny::db()->getQuery(
"SELECT * FROM users WHERE created_at > ? ORDER BY created_at DESC",
[$since]
);
}
tiny::cache()->remember() is the standard pattern for hot lookups:
public function byId(int $id): ?array
{
return tiny::cache()->remember("user:$id", 60, function () use ($id) {
return tiny::db()->getOne('users', ['id' => $id]);
}) ?: null;
}
public function invalidate(int $id): void
{
tiny::cache()->delete("user:$id");
}
Use tiny::cache()->deleteByPrefix("user:") to nuke an entire family of keys at once.
public function transfer(int $fromId, int $toId, float $amount): bool
{
$pdo = tiny::db()->getPdo();
$pdo->beginTransaction();
try {
tiny::db()->execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", [$amount, $fromId]);
tiny::db()->execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", [$amount, $toId]);
$pdo->commit();
return true;
} catch (\Throwable $e) {
$pdo->rollBack();
tiny::log($e->getMessage());
return false;
}
}
UserModel, OrderModel, InvoiceModel — not a god-object.isValid() before any write.