Personal Catalogue: PHP & MySQL — Chapter 7, Exercise 3 ==================================================== TASK Build view_item.php, then visit it with a real item's own id, and separately with an id you know doesn't exist (e.g. 999999). Confirm the second case shows "Item not found." rather than a blank page or a PHP error, and explain why $stmt->fetch() returning false is what makes the if (!$item) check work. SOLUTION Visiting view_item.php?id=1 (a real item, e.g. "Fluent Python") shows the full detail page — title, type, creator, format detail, release year, and tags, with the Edit and Back to Catalogue links at the bottom. Visiting view_item.php?id=999999 (an id that doesn't exist in the items table) shows the plain text "Item not found." with an HTTP 404 status code (visible via a browser's own network/developer tools, or by checking the response headers with curl -I), and nothing else — no PHP warnings, no partially-rendered page. Explanation of why $stmt->fetch() returning false makes the check work: PDOStatement::fetch() returns the next row of a result set as an array, or the boolean value false once there are no more rows to return. When the WHERE items.id = :id clause matches zero rows (as it does for id 999999, which was never inserted), the query itself runs successfully — it's a completely valid SQL query, it just matches no data — and fetch() returns false on its very first call, since there was never a first row to return. PHP's !$item negation treats false as falsy (correctly triggering the if block), the exact same as it would for null or 0 — but here it's specifically the real, documented return value of a query matching nothing, not a null coming from a nullable column the way it did for Chapter 3's creator/format_detail fields. The if (!$item) check is what stops the rest of the page (which assumes $item is a real array with real keys like $item['title']) from ever running against data that doesn't exist, which is exactly what would otherwise produce a PHP warning like "Trying to access array offset on value of type bool." WHY THIS WORKS AS AN ANSWER ---------------------------- It tests both the real-id and the nonexistent-id case directly rather than only the happy path, and correctly identifies fetch()'s own real, documented false-on-no-rows return value as the specific mechanism the guard clause depends on, rather than a vague "it checks if the item exists."