Fast, Lightweight, Independent Query Builder
A high-performance Active Record / ORM for MySQL, MariaDB, PostgreSQL, and SQLite. Zero framework dependencies, minimal footprint, maximum speed.
📖 Documentation · GitHub
use Simsoft\DB\Model;
class User extends Model
{
protected string $table = 'user';
protected array $fillable = ['name', 'email', 'status'];
}
// Query with fluent builder
$users = User::find()
->where('status', 'active')
->with('posts.comments') // nested eager loading
->orderBy('name')
->get();
// CRUD
$user = new User(['name' => 'John', 'email' => 'john@example.com']);
$user->save();The name says it all — Fast, Lightweight, Independent Query Builder.
- Fast — One object per query, zero-allocation fast path, prepared statement caching. No other PHP ORM builds query this lean.
- Lightweight — ~100KB install size, zero dependencies. No service containers, no config files, no boot process.
- Independent — Standalone library with no framework coupling. One
composer require, oneConnection::add()call, done. - Query Builder — Fluent, expressive API that compiles directly to optimized SQL without intermediate object layers.
- You need MSSQL or Oracle support
- You need schema migrations (use Phinx or doctrine/migrations)
- You need a Data Mapper pattern (use Doctrine or Cycle ORM)
- You need a massive ecosystem of community packages (use Eloquent)
For a detailed feature-by-feature comparison with Eloquent, Doctrine, Yii3, Cycle ORM, and Propel, see the Comparison Guide.
- PHP 8.4+
- MySQL 5.7+ / MariaDB 10.3+ / PostgreSQL 12+ / SQLite 3.39+
- ext-pdo (required) or ext-mysqli (optional alternative for MySQL)
composer require simsoft/fliqrequire "vendor/autoload.php";
use Simsoft\DB\Connection;
Connection::add('mysql', [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'my_app',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
]);use Simsoft\DB\Model;
use Simsoft\DB\Relation;
class Post extends Model
{
protected string $table = 'post';
protected array $fillable = ['title', 'content', 'user_id'];
public function author(): Relation
{
return $this->hasOne(User::class, ['id' => 'user_id']);
}
public function comments(): Relation
{
return $this->hasMany(Comment::class, ['post_id' => 'id']);
}
}// Find by primary key
$post = Post::findByPk(1);
// Query with conditions
$posts = Post::find()
->where('status', 'published')
->with('author', 'comments')
->orderBy('created_at', 'DESC')
->limit(10)
->get();
// Create
$post = new Post(['title' => 'Hello', 'content' => 'World']);
$post->save();
// Update
$post->title = 'Updated';
$post->save();
// Delete
$post->delete();- Zero-Allocation Query Building
- Nested Eager Loading
- Conditional Queries
- JSON Column Queries
- Multi-Column Conditions
- Transactions
- Soft Deletes & Timestamps
- Model Events
- Query Result Caching
- Read/Write Connection Splitting
- Development Tools
The most common query patterns (where, in, like, between, orderBy, select) build SQL strings directly without creating intermediate objects. One ActiveQuery object handles everything.
// 4 queries total, regardless of record count
$users = User::find()->with('posts.comments.author')->get();$users = User::find()
->when($search !== null, fn($q) => $q->like('name', "%$search%"))
->unless($isAdmin, fn($q) => $q->where('published', true))
->get();// Auto JSON extraction via -> notation (works in where, in, orderBy, etc.)
User::find()->where('preferences->dining->meal', 'salad')->get();
User::find()->in('preferences->dining->meal', ['pasta', 'salad'])->get();
User::find()->orderBy('meta->score', 'DESC')->get();
// JSON methods
User::find()->jsonContains('tags', 'php')->get(); // array contains value
User::find()->jsonNotContains('tags', 'java')->get(); // array excludes value
User::find()->jsonHas('meta->address')->get(); // key exists
User::find()->jsonMissing('meta->foo')->get(); // key missing
User::find()->jsonLength('tags', '>', 2)->get(); // array length
// Aliases (whereJson* style)
User::find()->whereJsonContains('tags', 'php')->get();
User::find()->whereJsonDoesntContain('tags', 'java')->get();
User::find()->whereJsonContainsKey('meta->address')->get();
User::find()->whereJsonDoesntContainKey('meta->foo')->get();
User::find()->whereJsonLength('tags', '>', 2)->get();// Match ANY column (OR logic)
User::find()->whereAny(['name', 'email', 'phone'], 'like', '%john%')->get();
// → WHERE (name LIKE ? OR email LIKE ? OR phone LIKE ?)
// Match ALL columns (AND logic)
Post::find()->whereAll(['title', 'body'], 'like', '%Laravel%')->get();
// → WHERE (title LIKE ? AND body LIKE ?)
// Match NONE of the columns
Post::find()->whereNone(['title', 'body'], 'like', '%spam%')->get();
// → WHERE NOT (title LIKE ? OR body LIKE ?)User::transaction(function () {
$user = new User(['name' => 'John', 'email' => 'john@example.com']);
$user->save();
$post = new Post(['user_id' => $user->id, 'title' => 'First Post']);
$post->save();
return true; // commit
});
// Return false (or don't return true) to roll backclass User extends Model
{
use SoftDeletes, Timestamps;
protected string $table = 'user';
}
$user->delete(); // sets deleted_at
$user->restore(); // clears deleted_at
User::withTrashed()->get(); // includes deleted// Register event listeners
User::on('creating', function (User $user) {
$user->slug = strtolower($user->name);
});
User::on('deleting', function (User $user) {
if ($user->role === 'admin') return false; // cancel deletion
});
// Observer class
User::observe(new AuditObserver());use Simsoft\DB\Cache\QueryCache;
use Simsoft\DB\Cache\ArrayCache;
QueryCache::setDriver(new ArrayCache());
// Cache results for 60 seconds
$users = User::find()->where('active', 1)->cache(60)->get();Connection::add('mysql', [
'driver' => 'mysql',
'database' => 'myapp',
'read' => ['host' => 'replica.db.internal'],
'write' => ['host' => 'primary.db.internal'],
]);
// SELECT auto-routes to read, INSERT/UPDATE/DELETE to write// N+1 detection
QueryMonitor::enable();
// Query logging with timing
QueryLogger::enable();
$queries = QueryLogger::getQueries();
$slowest = QueryLogger::getSlowestQuery();
// Index advisor
IndexAdvisor::suggestSQL();- Getting Started — Connections, drivers, raw queries, DB facade, monitoring
- Query Builder — Fluent API, conditions, joins, JSON, aggregation, scopes, unions
- Active Record — Models, CRUD, casting, hooks, events, soft deletes, timestamps
- Relations — hasOne, hasMany, via, viaTable, eager loading, whereHas
- Advanced Features — Caching, pagination, global scopes, batch operations, read/write split
- Collections — Lazy iteration, filter, map, reduce, indexBy, groupBy, batch processing
- Comparison — Feature comparison with Eloquent, Doctrine, Yii3, Cycle, Propel
- Cheatsheet — Quick reference for all common operations
FLIQ is licensed under the MIT License. See the LICENSE file for details.