Last updated: July 2026
An ORM — Object-Relational Mapper — is a library that lets you work with database rows as objects in your programming language. Instead of writing SQL strings and mapping result arrays by hand, you call methods on classes, and the ORM writes the SQL.
The same query, with and without an ORM
Raw PHP + PDO:
$stmt = $pdo->prepare('SELECT * FROM users WHERE status = ? ORDER BY created_at DESC LIMIT 10');
$stmt->execute(['active']);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
Laravel’s Eloquent ORM:
$users = User::where('status', 'active')->latest()->take(10)->get();
Same result — but the ORM version gives you User objects with relationships, casting, and events attached, and the parameter binding (SQL-injection protection) happens automatically.
Where ORMs shine: relationships
// Eloquent: a post's comments, and each comment's author — no JOIN written
$post = Post::with('comments.author')->find(1);
foreach ($post->comments as $comment) {
echo $comment->author->name;
}
The ORM issues efficient queries behind the scenes (with() prevents the N+1 problem) and hydrates a ready-to-use object graph.
The major ORMs by language
| Language | ORM(s) |
|---|---|
| PHP | Eloquent (Laravel), Doctrine (Symfony) |
| Python | SQLAlchemy, Django ORM |
| JavaScript/TypeScript | Prisma, TypeORM, Sequelize, Drizzle |
| Java | Hibernate (JPA) |
| C# | Entity Framework Core |
| Ruby | Active Record (Rails) |
Two design flavors exist: Active Record (the model class itself saves/queries — Eloquent, Rails) is more convenient; Data Mapper (a separate manager persists plain objects — Doctrine, SQLAlchemy classic) separates concerns better in large domains. For most projects the framework’s default is the right answer.
Honest pros and cons
Pros: much less boilerplate, injection-safe by default, migrations and schema versioning, portable across database engines, relationships and eager loading, testability.
Cons: the generated SQL can be inefficient if you don’t watch it (the N+1 query problem is the classic), complex reporting queries fight the abstraction, and there’s a learning curve on top of SQL — not instead of it. You still need to read EXPLAIN output when something’s slow.
When to drop to raw SQL
Every good ORM has an escape hatch — use it for heavy aggregations, window functions, or bulk operations:
$totals = DB::select('SELECT region, SUM(total) t FROM orders GROUP BY region');
Rule of thumb: ORM for the 95% of CRUD and relationship traversal; raw SQL for the 5% where you’re really programming the database. If you’re starting with Laravel, Eloquent’s accessors and casting are the next concepts worth learning — see Laravel accessors and mutators.
