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.
| Concept | What it protects | Decision rule |
|---|---|---|
| Foreign key | Referential integrity | Enforce every relationship the database owns. |
| Normalization | Single source of truth | Separate facts that change independently. |
| Index | Efficient lookup/order | Design 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.
- Describe the relational model boundary: its inputs, outputs, invariants, and expected failures.
- Implement the smallest happy path behind an explicit contract.
- Add validation and translate low-level failures into language the caller understands.
- Exercise the boundary with realistic data, then inspect output, logs, and resource cleanup.
- Refactor only after behavior is protected by a repeatable check.
Make the boundary visible
Guided code lab
Model many-to-many enrollment
The junction table gives the relationship its own timestamp and uniqueness rule.
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.
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
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
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.