eril / db-model-entity
A lightweight, fluent, and efficient database toolkit separating query logic from data mutations.
Requires (Dev)
- phpunit/phpunit: ^10.0
Suggests
- eril/tbl-class: Recommended for automated table-to-class mapping and strict database schema sync features.
README
A lightweight, fluent, and highly efficient PHP Database Toolkit designed to separate raw CRUD persistence operations from advanced data querying. By splitting responsibilities into distinct execution layers, DBME gives you Active Record features without the overhead or architectural pollution of typical heavy ORMs.
Key Architectural Concepts
Unlike monolith ORMs, DBME isolates its database operations into three specialized architectural pillars via a unified entry facade:
SqlExecutor(Direct CRUD): Handles targeted database write routines (create,update,delete) and primary key lookups without builder baggage.ModelQuery(Advanced Querying): A fluent, stateful query builder dedicated entirely to complex data filtration, aggregations, and subquery isolation (e.g., smart counting withGROUP BY).Entity&EntityCollection(Domain Layer): Encapsulates single rows into memory-efficient objects equipped with active dirty-state tracking (preventing redundant database roundtrips) and rich array capabilities.
Architecture Overview
graph TD
Facade[ModelEntity Facade] -->|::table| Executor[SqlExecutor Layer]
Facade -->|::query| MQ[ModelQuery Layer]
Executor -->|Pure Mutation| DB[(Database Server)]
MQ -->|Fluent Selects| DB
DB -.->|Hydrates Single Row| Entity[Entity Object]
DB -.->|Hydrates Multiple Rows| Collection[EntityCollection]
classDef primary fill:#2563eb,stroke:#1e40af,color:#fff,stroke-width:2px;
classDef secondary fill:#475569,stroke:#334155,color:#fff,stroke-width:2px;
classDef storage fill:#16a34a,stroke:#166534,color:#fff,stroke-width:2px;
class Facade primary;
class Executor,MQ secondary;
class DB,Entity,Collection storage;
Loading
Installation
Install the package via Composer (adjust repository path as needed):
composer require eril/db-model-entity
Quick Start & Usage
1. Connection Initialization
You can initialize a brand new standalone PDO instance directly or register an existing connection/lazy-loading closure (ideal for framework integrations).
use Eril\DBME\Database\DB; // Option A: Direct Connection DB::connect('mysql:host=127.0.0.1;dbname=app_db;charset=utf8mb4', 'root', 'secret'); // Option B: Lazy-Loading Connection Resolver (e.g., sharing a framework instance) DB::registerConnection(function() { return Container::get('pdo'); }); // Option C: Lazy-Loading Connection Resolver (e.g., Own Database Class) DB::registerConnection([Database::class, "getConnection"]);
2. Standard CRUD Operations (::table)
Use the table entry point when mutating data or performing explicit row mutations. It returns a dedicated SqlExecutor instance.
use Eril\DBME\ModelEntity; // Create a new record (Returns an Entity object) $user = ModelEntity::table('users')->create([ 'name' => 'John Doe', 'email' => 'john@example.com', 'status' => 'pending' ]); // Update multiple target records based on conditions ModelEntity::table('users') ->where('status', '=', 'pending') ->update(['status' => 'inactive']); // Delete target records safely ModelEntity::table('users')->delete(15); // Delete by ID
3. Advanced Querying (::query)
Use the query entry point when you need a fluent query builder interface, conditional filters, pagination, or database calculations. It returns a ModelQuery instance.
use Eril\DBME\ModelEntity; // Fetch a collection using fluent criteria $users = ModelEntity::query('users', 'u') ->where('u.status', '=', 'active') ->where('u.age', '>', '21') ->orderBy('u.created_at', 'DESC') ->get(); // Returns an EntityCollection // Run high-performance aggregates directly on the server level $averageAge = ModelEntity::query('users')->aggregate('AVG', 'age'); $totalRevenue = ModelEntity::query('orders')->aggregate('SUM', 'total_amount'); // Smart counting protects against broken counts when using GROUP BY $count = ModelEntity::query('orders') ->groupBy('customer_id') ->count();
4. Active Record & Memory Snapshots (Entity)
When an Entity is loaded from the database, it captures an internal snapshot of its database state. Calling update() executes dirty-checking to compile an optimal SQL payload, updating only mutated properties.
// Modifying object properties dynamically $user = ModelEntity::query('users')->first(); $user->name = 'Jane Doe'; // The update routine safely merges and checks both manual changes and arguments // If nothing changed compared to the loading snapshot, no database query is executed. $user->update(['status' => 'verified']); // Self-deletion capabilities $user->delete();
5. Collection Operations (EntityCollection)
The EntityCollection provides high-utility, in-memory array manipulation tools without querying the database server redundantly.
$activeUsers = $users->filter(fn($user) => $user->status === 'active'); // Pluck a specific column as a scalar array $emails = $users->pluck('email'); // ['john@example.com', 'jane@example.com'] // Look up a specific record loaded into memory by any key-value criterion $targetUser = $users->find('id', 42); if (!$users->isEmpty()) { $firstOne = $users->first(); }
6. Atomic Database Transactions
Wrap complex, cross-table mutation streams securely inside database transactions. Any uncaught exception automatically rolls back changes.
use Eril\DBME\Database\DB; use Eril\DBME\ModelEntity; DB::transaction(function () { ModelEntity::table('accounts')->where('id', 1)->update(['balance' => 400]); ModelEntity::table('accounts')->where('id', 2)->update(['balance' => 800]); // If an error occurs here, both balance changes are completely rolled back. });
Code Quality Standards
This library is designed keeping static analysis tools (PHPStan, Psalm) in mind. All classes include strict inline generics documentation, precise typehints, and clear object property mappings.
License
This toolkit is open-sourced software licensed under the MIT License.