Models, relationships, and pagination
Elang Data is a small model and repository layer over Doctrine DBAL. It makes common record operations explicit without claiming the full behavior of a mature ORM.
Define a model
php elang make:model Post --table=posts
Edit the generated class:
final class Post extends Elang\Data\Model
{
protected const TABLE = 'posts';
protected const FILLABLE = ['title', 'author_id', 'published'];
protected const HIDDEN = ['internal_note'];
protected const CASTS = ['id' => 'int', 'published' => 'bool'];
}
FILLABLE explicitly allows writes. Unexpected fields are rejected, rather than becoming assignments accidentally. HIDDEN excludes selected fields from JSON and array output; it does not replace authorization.
Read and write records
Inject Elang\Data\Models into your service:
$posts = $models->of(Post::class);
$post = $posts->create(['title' => 'Hello', 'author_id' => 1, 'published' => 0]);
$mine = $posts->where('author_id', 1);
$mine->update($post->id, ['title' => 'A clearer title']);
Repository filters are immutable: where and orderBy return a new scoped repository. Scoped update and delete include the ownership conditions in the statement. A create call must match every equality scope already applied to its repository.
Safe identifiers and values
Table and column identifiers must be simple application-controlled names. Operators and sort directions use allowlists. User values are bound as parameters. Do not offer arbitrary SQL fragments as a user-controlled search or sort option.
Pagination
$page = $posts->where('published', 1)->orderBy('id', 'DESC')->paginate(15, 1);
Paginator exposes items, total, perPage, currentPage, and lastPage. Its JSON representation contains data and meta. PageRequest validates an incoming page number, and per-page limits are bounded.
Relationships
Relationships are explicit queries, not invisible property loads:
$comments = $models->hasMany($post, Comment::class, 'post_id')->get();
$author = $models->belongsTo($post, UserProfile::class, 'author_id');
Plan eager loading and query counts for large collections. Studio helps inspect the queries actually executed. This layer does not currently provide a unit of work, identity map, or automatic graph persistence.
Factories and seeders
Factory accepts a repository and a definition callback. It supports bounded batch creation and overrides. Seeder classes implement Elang\Data\Seeder and run through php elang db:seed 'App\Database\DemoSeeder'. Production seeding requires review and the --force flag.