Lightweight, high-performance PHP 8.2+ ORM with native attribute mapping, LINQ-style query builder, and automatic entity generation from database.
| Feature | Description |
|---|---|
| PHP 8.2 Attributes | #[Entity], #[Column], #[Id], #[HasMany]... — zero config |
| LINQ-Style QueryBuilder | where(), firstOrFail(), single(), distinct(), toList() |
| Unit of Work | Batch INSERT/UPDATE/DELETE in single transaction |
| Dirty Checking | Only UPDATE changed fields (snapshot pattern) |
| Identity Map | Same entity = same instance, no duplicate queries |
| Eager Loading | with('posts') — batch queries, avoid N+1 |
| Read/Write Splitting | Auto-route SELECT to read replicas |
| Entity Generator | Reverse-engineer DB tables → PHP entity classes |
| Auto Timestamps | #[CreatedAt], #[UpdatedAt] auto-managed |
Minimum: PHP 8.2+ (PHP 7.4 is NOT supported)
LiteORM relies heavily on modern PHP features that have no backwards-compatible alternatives:
| Feature Used | Minimum PHP | Why? |
|---|---|---|
Native Attributes (#[Entity]) |
8.0 | Core mapping mechanism — replaces XML/YAML/annotations |
readonly properties |
8.1 | Immutable metadata objects |
| Constructor property promotion | 8.0 | Clean attribute constructors |
Union types (string|array) |
8.0 | Flexible API signatures |
match expression |
8.0 | Type resolution, SQL generation |
Enums, str_ends_with(), etc. |
8.0–8.2 | Various utility functions |
Important
PHP 7.4 reached End-of-Life on November 28, 2022. If you need PHP 7.x support, consider Doctrine ORM (annotation-based) or Eloquent (convention-based).
- Hiệu năng cực cao (High Performance): Cơ chế Prepared Statement Cache (tái sử dụng PDOStatement) và Batch Insert (O(1) prepare) giúp query lặp lại cực khứ.
- Không cần cấu hình (Zero Config): Khai báo mapping trực tiếp trên class bằng PHP 8.2 Attributes (
#[Entity],#[Column]), code gập gọn, dễ đọc. - Tối ưu RAM & DB (Memory & DB Optimized):
- Identity Map đảm bảo mỗi ID chép ra 1 instance duy nhất trên memory.
- Dirty Checking (chụp snapshot) đảm bảo chỉ gen câu lệnh
UPDATEcho những trường (fields) thực sự bị sửa đổi. - AsNoTracking: Hỗ trợ ngắt tracking hoàn toàn cho các truy vấn chỉ đọc (read-only), tiết kiệm CPU và bộ nhớ cực tốt.
- Ngăn chặn triệt để N+1 Queries: Eager Loading (
with()) sử dụng cơ chế gom ID (WHERE IN) để select dữ liệu quan hệ, chỉ tốn đúng 2 query thay vì N+1. - Trải nghiệm code mượt mà như C# LINQ: Collection phong phú các method như
firstOrFail(),single(),whereNotIn()dành cho developer quen C# / LINQ. - Tạo Entity tự động (Entity Generator CLI): Có sẵn tool console line (
bin/liteorm generate) reverse-engineering từ Database thẳng ra file PHP Entity tích hợp đầy đủ Attributes.
- Rào cản môi trường: Yêu cầu bắt buộc PHP 8.2+, hoàn toàn không tương thích với dự án PHP 7.x cũ.
- Chưa có Versioning Migrations: Framework mới chỉ hỗ trợ build bảng (
createTable()) và gen Entity CLI. Chưa có flow chạy file Up/Down migrations (như Phinx hay Laravel). - Database Dialects: Mặc dù hoạt động hoàn hảo với MySQL, SQLite nhưng vì QueryBuilder đang dùng toán tử khá tiêu chuẩn, các query filter có syntax độc lạ trên SQL Server / PostgreSQL có thể cần custom thêm.
composer require kzxl/liteormuse LiteORM\Attribute\{Entity, Table, Column, Id, AutoIncrement, CreatedAt, UpdatedAt, HasMany};
#[Entity]
#[Table('users')]
class User
{
#[Id, AutoIncrement]
public int $id;
#[Column(length: 100)]
public string $name;
#[Column(unique: true)]
public string $email;
#[Column(nullable: true)]
public ?int $age = null;
#[CreatedAt]
public ?\DateTimeImmutable $createdAt = null;
#[UpdatedAt]
public ?\DateTimeImmutable $updatedAt = null;
#[HasMany(target: Post::class, foreignKey: 'user_id')]
public array $posts = [];
}$em = new EntityManager('mysql:host=localhost;dbname=myapp', 'root', 'pass');
// Create
$user = new User();
$user->name = 'John';
$user->email = 'john@example.com';
$em->persist($user);
$em->flush(); // INSERT + auto-set $user->id + timestamps
// Read
$user = $em->find(User::class, 1);
// Update (only dirty fields)
$user->name = 'Updated';
$em->flush(); // UPDATE users SET name='Updated' WHERE id=1
// Delete
$em->remove($user);
$em->flush();// First (= FirstOrDefault)
$user = $em->query(User::class)
->where('email', 'john@example.com')
->first(); // returns null if not found
// FirstOrFail (= LINQ First)
$user = $em->query(User::class)
->where('id', 1)
->firstOrFail(); // throws RuntimeException if not found
// Single (= SingleOrDefault)
$user = $em->query(User::class)
->where('email', 'unique@test.com')
->single(); // throws if >1 result
// SingleOrFail (= LINQ Single)
$user = $em->query(User::class)
->where('id', 1)
->singleOrFail(); // throws if 0 or >1
// Complex queries
$users = $em->query(User::class)
->where('age', '>', 18)
->whereNotIn('name', ['Admin', 'System'])
->orderBy('name')
->distinct()
->take(10) // alias for limit()
->skip(20) // alias for offset()
->toList(); // alias for get()
// Last
$newest = $em->query(User::class)
->orderBy('created_at', 'ASC')
->last(); // auto-reverses to DESC, returns first
// Aggregates
$count = $em->query(User::class)->where('active', true)->count();
$avgAge = $em->query(User::class)->avg('age');
$maxAge = $em->query(User::class)->max('age');
// Exists / Any
if ($em->query(User::class)->where('email', $email)->exists()) { ... }
// Bulk operations
$em->query(User::class)->where('active', false)->delete();
$em->query(User::class)->where('role', 'guest')->update(['role' => 'user']);// HasMany
$users = $em->query(User::class)
->with('posts')
->get();
// Only 2 queries: SELECT * FROM users + SELECT * FROM posts WHERE user_id IN (...)
// BelongsTo
$posts = $em->query(Post::class)
->with('author')
->get();$em = new EntityManager([
'write' => 'mysql:host=master;dbname=app',
'read' => [
'mysql:host=replica1;dbname=app',
'mysql:host=replica2;dbname=app',
],
], 'user', 'pass');
// SELECT → auto-routed to replica (round-robin)
// INSERT/UPDATE/DELETE → masterLiteORM includes a CLI tool to reverse-engineer database tables into PHP entity classes automatically.
# Generate entities for ALL tables
php bin/liteorm generate --dsn="mysql:host=127.0.0.1;dbname=app" --user=root --pass=secret --namespace="App\Entity" --out="./src/Entity"
# Generate for a specific table ONLY
php bin/liteorm generate --dsn="sqlite:database.sqlite" --namespace="App\Entity" --out="./src/Entity" --table="users"If you are querying data only for display and do not intend to update it, use asNoTracking(). This bypasses the Unit of Work identity map, saving memory and CPU time during snapshot creation.
$users = $em->query(User::class)
->where('role', 'admin')
->asNoTracking()
->toList();
// Changes will NOT be saved
$users[0]->name = 'Hacked';
$em->flush(); // (0 queries executed)| C# LINQ | LiteORM PHP | Note |
|---|---|---|
Where(x => ...) |
->where('col', '>', 10) |
Fluent, supports operator |
FirstOrDefault() |
->first() |
Returns null |
First() |
->firstOrFail() |
Throws |
SingleOrDefault() |
->single() |
Throws if >1 |
Single() |
->singleOrFail() |
Throws if ≠1 |
Last() |
->last() |
Auto-reverse order |
Any() |
->exists() |
Returns bool |
Count() |
->count() |
|
Sum/Avg/Max/Min |
->sum/avg/max/min() |
|
Take(n) |
->take(n) or ->limit(n) |
|
Skip(n) |
->skip(n) or ->offset(n) |
|
Distinct() |
->distinct() |
|
ToList() |
->toList() or ->get() |
|
Contains() |
->whereIn() |
|
OrderBy/Desc |
->orderBy('col', 'DESC') |
|
GroupBy |
->groupBy('col') |
|
Select() |
->select('col1', 'col2') |
|
AsNoTracking() |
->asNoTracking() |
Bypasses UoW tracking |
LiteORM/
├── bin/
│ └── liteorm # CLI Generator tool
├── src/
│ ├── Attribute/ # PHP 8.2 attributes (Entity, Column, Id, Relations...)
│ ├── Metadata/ # AttributeReader, EntityMetadata, ColumnMetadata
│ ├── Connection/ # ConnectionManager (R/W split, pooling)
│ ├── Query/ # QueryBuilder (LINQ-style fluent API)
│ ├── Schema/ # EntityGenerator (reverse DB → PHP)
│ └── EntityManager.php # Main entry point (UoW, Identity Map)
├── tests/ # PHPUnit test suite (67 tests)
├── composer.json
└── phpunit.xml
Design Principles:
- Zero config — PHP attributes replace XML/YAML mapping
- Dirty checking — snapshot pattern, only UPDATE changed fields
- Identity map — one entity instance per primary key
- Batch eager loading —
WHERE IN (...)instead of N+1 queries - Type safety — strict types, PHPDoc generics
composer install
vendor/bin/phpunitApache License 2.0 — see LICENSE.