atk4/data
ATK Data is a PHP data model abstraction that separates business logic from UI and persistence. Works with SQL/NoSQL/APIs, supports relations, expressions, aggregation, and user actions with ACL metadata—integrates easily with ATK UI and ATK API.
Installation:
composer require atk4/data
Requires atk4/core as a dependency.
Define a Basic Model:
use Atk4\Data\Model;
$db = new \Atk4\Data\Persistence\Sql('mysql:host=localhost;dbname=test', 'user', 'pass');
$client = new Model($db);
$client->addField('name');
$client->addField('email');
First Use Case:
// Insert a record
$client->insert(['name' => 'John Doe', 'email' => 'john@example.com']);
// Fetch records with a condition
$vipClients = $client->addCondition('is_vip', true)->getCollection();
Model Definition:
class Client extends Model
{
protected function init(): void
{
parent::init();
$this->addField('name');
$this->addField('email');
$this->addField('is_vip')->type('boolean');
}
}
init() for field definitions and relations.type('atk4_money')) for validation and formatting.Querying Data:
$clients = (new Client($db))
->addCondition('is_vip', true)
->addOrder('name')
->getCollection();
addCondition(), addOrder(), and addJoin() for complex queries.$client->addExpression('full_name', ['expr' => '[name] || \' \' || [surname]']);
Relations:
class Invoice extends Model
{
protected function init(): void
{
parent::init();
$this->hasOne('client_id', ['model' => 'Client']);
$this->addField('total')->type('atk4_money');
}
}
hasOne(), hasMany(), or belongsTo().$invoice = (new Invoice($db))->load(1);
$clientName = $invoice->ref('client_id')->getOne('name');
Actions:
$client->addAction('send_email', ['email' => 'john@example.com'])
->perform();
init():
$this->addAction('archive', ['confirm' => true]);
$this->addAction('delete')->setPermission('admin');
Integration with UI/API:
// UI (Agile UI)
Crud::addTo($app)->setModel(new Client($db), ['name', 'email']);
// API (Agile API)
$api->rest('/clients', new Client($db));
Crud, Grid, Form).Agile\Api to expose models as REST endpoints.Service Provider Setup:
// config/app.php
'providers' => [
Atk4\Laravel\Ad\AdServiceProvider::class,
],
Install the Laravel AD package for seamless integration.
Model Binding:
// In a Laravel controller
$client = app(Client::class)->setPersistence($db);
Use dependency injection or the app() helper to resolve models.
Query Builder Integration:
$query = Client::query()->where('is_vip', true);
Extend Laravel’s query builder with ATK Data methods via traits or macros.
Middleware for ACL:
// app/Http/Middleware/CheckPermission.php
public function handle($request, Closure $next)
{
$model = app(Client::class);
if (!$model->checkPermission($request->user(), 'edit')) {
abort(403);
}
return $next($request);
}
Event Hooks:
// Listen to model events (e.g., afterSave)
$client->on('afterSave', function ($model) {
Log::info("Client saved: " . $model->get('name'));
});
Eager Loading:
$client = (new Client($db))->load(1, ['invoice']);
Load related data in a single query using load() with relation names.
Batch Operations:
$client->update(['is_vip' => true], ['id' => [1, 2, 3]]);
Use bulk methods like update(), delete(), or insert() for efficiency.
Persistence Caching:
$db = new \Atk4\Data\Persistence\Sql('mysql:...');
$db->setCache(new \Atk4\Data\Persistence\Cache\File());
Enable caching for repeated queries (e.g., Redis or file-based).
Aggregate Queries:
$total = (new Client($db))
->action('fx', ['sum', 'total'])
->getOne();
Offload aggregation to the database with action('fx', ['aggregate', 'field']).
Lazy Loading Overhead:
getCollection() with load() or preload() for related data.$clients = (new Client($db))->getCollection(['invoice']);
Permission Misconfiguration:
$this->addAction('delete')->setPermission('admin');
Expression Syntax Errors:
// Wrong: Missing brackets
$this->addExpression('profit', ['expr' => '[revenue] - cost']);
// Correct:
$this->addExpression('profit', ['expr' => '[revenue] - [cost]']);
Circular References:
hasOne + belongsTo). Use ->setLazy() to disable eager loading for one side:
$this->hasOne('client_id', ['model' => 'Client'])->setLazy();
Persistence-Specific Quirks:
action('fx') may not work with NoSQL). Check the persistence documentation.Enable SQL Logging:
$db->setDebug(true);
Logs all generated SQL queries to storage/logs/atk4-data.log.
Inspect Model Structure:
echo $model->dump();
Outputs the model’s fields, relations, and conditions for debugging.
Use getOne() vs getCollection():
getOne() returns a single value (e.g., sum(total)).getCollection() returns an array of records. Mixing them can cause type errors.Handle Exceptions:
try {
$model->perform('delete');
} catch (\Atk4\Data\Exception\Validation $e) {
echo $e->getMessage();
}
Catch Validation exceptions for failed actions or data issues.
class CustomField extends \Atk4\Data\Field
{
public function validate($value)
{
return $value === 'expected';
}
}
Extend `\Atk4\Data\How can I help you explore Laravel packages today?