Build a full data access layer using PDO: prepared statements with named parameters, transactions with rollback, the Repository pattern for decoupled data access, query builder basics, and performance with fetchAll(PDO::FETCH_CLASS).
Background
PDO (PHP Data Objects) provides a database-agnostic API — the same code works with SQLite, MySQL, PostgreSQL, and others by changing only the DSN. The Repository pattern separates business logic from data access: OrderService calls $orderRepo->save($order) without knowing whether it's SQLite or PostgreSQL underneath. This makes testing easy (swap the real repo for an in-memory fake) and keeps domain logic clean.
Step 1: Schema, prepared statements & named parameters
💡 Always use prepared statements — never string-interpolate user input into SQL."SELECT * FROM products WHERE id = {$_GET['id']}" is a SQL injection vulnerability. With PDO named parameters (:id) or positional (?), the value is always treated as data, never as SQL. The database driver handles escaping. This is not about performance (though prepared statements are faster for repeated queries) — it is about security.
📸 Verified Output:
Step 2: Transactions with rollback + Repository pattern