{
    "slug": "orm",
    "term": "ORM — Object-Relational Mapper",
    "category": "database",
    "difficulty": "beginner",
    "short": "A library that maps database rows to PHP objects and vice versa — handling SQL generation, relationships, and lazy loading, at the cost of hiding query behaviour that can cause N+1 problems if used carelessly.",
    "long": "An ORM bridges the object-oriented PHP world and the relational database world. Instead of writing SQL, you work with PHP objects (Eloquent models, Doctrine entities) and the ORM generates appropriate SQL. Two main patterns: Active Record (Eloquent in Laravel) — the model is both the domain object and the database gateway; User::find(1) issues SELECT and returns a User object; Doctrine in Data Mapper mode — entities are plain PHP objects, a separate Repository class handles persistence. ORM advantages: no raw SQL boilerplate; database-agnostic code (switch MySQL to PostgreSQL by changing config); built-in relationships (hasMany, belongsTo) with lazy loading; migrations. ORM pitfalls: lazy loading causes N+1 queries (loading 100 posts then accessing post->author triggers 100 individual author queries); ORM-generated SQL is not always optimal; complex reporting queries are easier in raw SQL.",
    "aliases": [
        "ORM",
        "Eloquent",
        "Doctrine",
        "Active Record",
        "Data Mapper",
        "object-relational mapping"
    ],
    "tags": [
        "orm",
        "database",
        "eloquent",
        "doctrine",
        "php",
        "laravel",
        "sql"
    ],
    "misconception": "ORMs are slower than raw SQL and should be avoided in performance-sensitive code. ORMs generate correct SQL, and properly used with eager loading and indexes, perform identically to hand-written SQL. The performance issue is not the ORM itself but lazy loading causing N+1 queries — fixable with with() in Eloquent or JOIN FETCH in Doctrine. Use raw SQL only for complex reporting queries the ORM cannot express cleanly.",
    "why_it_matters": "ORMs are the default data access layer in Laravel and Symfony — understanding how they work prevents the most common performance antipattern in PHP applications. An Eloquent query like $posts = Post::all(); foreach ($posts as $post) { echo $post->author->name; } issues 1 + N database queries (one for posts, one per author). With $posts = Post::with('author')->get() it issues 2 queries total. Understanding the difference between lazy and eager loading is the single most impactful ORM knowledge for PHP developers.",
    "common_mistakes": [
        "Using lazy loading in loops — always eager-load relationships with with() when you know you will access them.",
        "Calling ->get() and then ->count() separately — use ->count() alone which generates SELECT COUNT(*), not SELECT * then PHP count().",
        "Using Model::all() on large tables — always add ->limit() or pagination to prevent loading entire tables into memory.",
        "Over-using ORMs for reporting queries with multiple JOINs and aggregations — raw SQL with DB::select() is often cleaner and more readable for complex analytics."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "database_indexes",
        "query_optimization",
        "database_connection_pool"
    ],
    "prerequisites": [],
    "refs": [
        "https://laravel.com/docs/eloquent",
        "https://www.doctrine-project.org/projects/doctrine-orm"
    ],
    "bad_code": "// N+1 — 1 query for posts + 1 per post for author\n$posts = Post::all(); // SELECT * FROM posts\nforeach ($posts as $post) {\n    echo $post->author->name; // SELECT * FROM users WHERE id = ? (×N)\n    echo $post->comments->count(); // SELECT * FROM comments WHERE... (×N)\n}",
    "good_code": "// Eager loading — 3 queries total regardless of post count\n$posts = Post::with(['author', 'comments'])->paginate(20);\n// SELECT * FROM posts LIMIT 20\n// SELECT * FROM users WHERE id IN (1,2,3...)\n// SELECT * FROM comments WHERE post_id IN (1,2,3...)\nforeach ($posts as $post) {\n    echo $post->author->name;     // no query — already loaded\n    echo $post->comments->count(); // no query — already loaded\n}",
    "quick_fix": "Replace lazy loading with eager loading: Post::with(['author', 'comments'])->get() instead of Post::all() — eliminates N+1 queries for those relationships",
    "severity": "medium",
    "effort": "low",
    "created": "2026-03-23",
    "updated": "2026-04-05",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/orm",
        "html_url": "https://codeclaritylab.com/glossary/orm",
        "json_url": "https://codeclaritylab.com/glossary/orm.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[ORM — Object-Relational Mapper](https://codeclaritylab.com/glossary/orm) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/orm"
            }
        }
    }
}