Phase 4 · Databases & PersistenceModule 24~50 min read

Data Modeling & Joins

Design normalized relationships and query them efficiently with joins, constraints, and indexes.

What you'll learn

Translate business relationships into keys, constraints, and efficient joins. A good model prevents impossible data and supports the questions the application must answer.

By the end of this lesson, you'll be able to:

  • Model one-to-many and many-to-many relationships
  • Select the correct join type
  • Detect N+1 queries and design useful indexes

Core mental model

Professional PHP is less about memorizing APIs and more about choosing a clear boundary for each responsibility. Use this table as a decision guide while reading the examples.

ConceptWhat it protectsDecision rule
Foreign keyReferential integrityEnforce every relationship the database owns.
NormalizationSingle source of truthSeparate facts that change independently.
IndexEfficient lookup/orderDesign from measured query predicates and sort order.

Professional workflow

Build the feature in small, verifiable steps. Each step leaves the system in a state you can test.

  1. Describe the relational model boundary: its inputs, outputs, invariants, and expected failures.
  2. Implement the smallest happy path behind an explicit contract.
  3. Add validation and translate low-level failures into language the caller understands.
  4. Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
  5. Refactor only after behavior is protected by a repeatable check.

Make the boundary visible

Name inputs, outputs, side effects, and failure cases before adding framework or infrastructure code. That habit keeps advanced PHP understandable as the application grows.

Guided code lab

Model many-to-many enrollment

The junction table gives the relationship its own timestamp and uniqueness rule.

schema.sql
CREATE TABLE students (
  id BIGINT PRIMARY KEY,
  email VARCHAR(255) NOT NULL UNIQUE
);
CREATE TABLE courses (
  id BIGINT PRIMARY KEY,
  title VARCHAR(180) NOT NULL
);
CREATE TABLE enrollments (
  student_id BIGINT NOT NULL REFERENCES students(id) ON DELETE CASCADE,
  course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
  enrolled_at TIMESTAMP NOT NULL,
  PRIMARY KEY (student_id, course_id)
);

Replace N+1 with one aggregate query

The left join keeps courses with zero enrollments and groups the relationship rows.

catalog.sql
SELECT c.id, c.title, COUNT(e.student_id) AS enrollment_count
FROM courses AS c
LEFT JOIN enrollments AS e ON e.course_id = c.id
WHERE c.published_at IS NOT NULL
GROUP BY c.id, c.title
ORDER BY c.title ASC;

Production practice

Contract

Write down cardinality, optionality, ownership, and deletion behavior before creating tables.

Verification

Use constraint tests and inspect query plans with realistic data volumes.

Operations

Monitor index size and write cost; remove redundant indexes as carefully as you add them.

Common failure mode

An index on every column slows writes and consumes memory without guaranteeing useful access. Composite index order must match actual query prefixes.

Independent workshop

Design a publishing schema for authors, articles, tags, comments, and revisions, then answer three reporting questions.

Your finished workshop must include:

  • ER diagram with cardinalities
  • DDL containing keys and deletion rules
  • Join queries plus their query-plan notes

Definition of done

Run the happy path and at least two failure paths, explain one design tradeoff in a short README, and leave the code formatted and ready for review.

Recap & quick check

Key takeaways

  • Constraints make the model enforceable.
  • Junction tables model many-to-many facts.
  • Joins assemble related data in one query.
  • Indexes follow workloads, not guesswork.

Quick check

1. Which join keeps unmatched left rows?

2. What causes N+1?

3. What should guide an index?

Keep the workshop: later phases deliberately build on these boundaries, so today's small example can become part of your portfolio architecture.