Building the CRUD Pages
Personal Catalogue: PHP & MySQL
Chapter 3 · Building the CRUD Pages
Chapter 2 built the real table structure. This chapter builds the first pages that
actually write to it — adding an item, editing an existing one, and deleting one —
using the PDO connection from Chapter 1 and the prepared-statement discipline this
site's own PHP Intermediate course (Chapter 4) already covers in depth. Tags and
search come later (Chapters 5 and 6); this chapter deliberately stays scoped to the
items table alone.
A Minimal Item-Type Picker
Every add/edit form needs to offer the four real item types from item_types
as a dropdown, rather than hard-coding them as literal strings in the form's own markup.
A tiny shared function keeps that list in one place:
Since item_types almost never changes at runtime, a plain unparameterised
query() is fine here — prepare()/execute() only
really earns its keep when real, external data is involved, which this call doesn't have.
Adding an Item
add_item.php does two jobs depending on the request method: on a
GET request it shows the empty form; on a POST request it
validates and inserts the submitted data. This is the same "one file, two branches"
pattern PHP Fundamentals' own Chapter 8 already introduced for $_SERVER['REQUEST_METHOD'].
creator, format_detail, release_year, and
notes are all nullable columns (Chapter 2). A DVD genuinely has no author,
and it's more honest for that field to store SQL NULL — "no value recorded"
— than an empty string, which reads as "the value is the empty string," a subtly
different and less accurate claim. The ternaries in the execute() array
make that translation explicit rather than leaving it to chance.
Editing an Item
edit_item.php follows the same GET/POST split, but GET now has to fetch the
existing row first (by id from the query string) so the form can be
pre-filled, and POST runs an UPDATE instead of an INSERT:
id, rather than silently rendering a blank
form that would submit an UPDATE against a row that was never actually loaded.
Deleting an Item
Delete is the simplest of the three, but it's the one most worth protecting against an accidental click — this project uses a plain confirmation link rather than a full modal, since a personal single-user catalogue doesn't need more ceremony than that:
On the listing page (built in full in Chapter 7), each row's own delete action is a tiny
<form method="post" action="delete_item.php"> containing just the hidden
id field and a submit button — never a plain <a href> link.
DELETE FROM items ... triggered by a plain link means the browser (or a
search-engine crawler, or a link-preview bot) could delete a real item just by fetching
that URL — a GET request is supposed to be "safe" (no side effects) by HTTP's own
convention, and browsers, proxies, and bots all rely on that convention. Requiring a POST
means the delete can only actually happen from a real form submission on this page.
Where Tags Fit Later
This chapter's own add/edit forms deliberately don't touch tags or
item_tags yet — Chapter 5 covers attaching and managing tags specifically,
once the core item CRUD is solid on its own. Building both at once would make it harder
to tell, if something breaks, whether the bug is in the item logic or the tag logic.
Hands-On Exercises
Wire up add_item.php against the real catalogue database and add three items: one book, one CD, and one DVD. Confirm all three appear correctly with SELECT * FROM items;, including that each row's unused columns (e.g. a DVD's own creator) are stored as NULL, not an empty string.
Complete the full edit_item.php form markup (the part left as a comment in this chapter), pre-filling every field's own value attribute from $item, then use it to change one of your three items' own release_year.
Explain, in your own words, why delete_item.php requires a POST request rather than allowing a plain link, and describe one real, concrete way a GET-based delete link could be triggered by something other than a deliberate click.
Chapter 3 Quick Reference
- add_item.php — GET shows the form, POST validates and runs a prepared
INSERT - edit_item.php — GET fetches the existing row and pre-fills the form, POST runs a prepared
UPDATE - delete_item.php — POST-only, running a prepared
DELETE, never triggerable via a plain link - Empty form fields — converted to SQL
NULL, not stored as empty strings, for every nullable column - Tags — deliberately deferred to Chapter 5, kept out of this chapter's own item CRUD
- Next chapter: Type-Specific Fields — one form, several real item shapes