diff --git a/.DS_Store b/.DS_Store index d29bd1f6..2ab15c45 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f4f4c77e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +04/.DS_Store diff --git a/03/03_make_it_maintainable/footer.php b/03/03_make_it_maintainable/footer.php new file mode 100644 index 00000000..6e359778 --- /dev/null +++ b/03/03_make_it_maintainable/footer.php @@ -0,0 +1,9 @@ + +
+

© 2026

+ + + diff --git a/03/03_make_it_maintainable/header.php b/03/03_make_it_maintainable/header.php new file mode 100644 index 00000000..b6a9b750 --- /dev/null +++ b/03/03_make_it_maintainable/header.php @@ -0,0 +1,13 @@ + + + + + + My PHP Page + + +

Welcome

+
diff --git a/03/03_make_it_maintainable/index.php b/03/03_make_it_maintainable/index.php index 5cb2b174..f73e81f5 100644 --- a/03/03_make_it_maintainable/index.php +++ b/03/03_make_it_maintainable/index.php @@ -1,34 +1,30 @@ ['title' => 'Home', 'content' => 'Welcome to my PHP page.'], + 'about' => ['title' => 'About', 'content' => 'This is the About section.'], + 'contact' => ['title' => 'Contact', 'content' => 'This is the Contact section.'], +]; +$current = $_GET['page'] ?? 'home'; +if (!isset($pages[$current])) { + $current = 'home'; +} ?> - - - - My PHP Page - - + -

Welcome

+

+

- - - - - - + diff --git a/05/assets/bitumi.png b/05/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/05/assets/bitumi.png differ diff --git a/05/includes/connect.php b/05/includes/connect.php new file mode 100644 index 00000000..bc9a2c3f --- /dev/null +++ b/05/includes/connect.php @@ -0,0 +1,18 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/05/includes/footer.php b/05/includes/footer.php new file mode 100644 index 00000000..29d99ae4 --- /dev/null +++ b/05/includes/footer.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/05/includes/header.php b/05/includes/header.php new file mode 100644 index 00000000..f42c9bd1 --- /dev/null +++ b/05/includes/header.php @@ -0,0 +1,29 @@ + + + + + + + Week 4 - Form Validation + + + + + + + + +
+

+ +

+ +
\ No newline at end of file diff --git a/05/index.php b/05/index.php new file mode 100644 index 00000000..c22158fb --- /dev/null +++ b/05/index.php @@ -0,0 +1,114 @@ + +
+

Order Online - Easy & Simple (And Totally Secure...) 🧁

+
+ + + +
+ Customer Information + + + + + + + + + + +
+ + + + +
+ Additional Comments + +

+
+ +

+
+ +

+ +

+ +
+
+ + + + + \ No newline at end of file diff --git a/05/orders.php b/05/orders.php new file mode 100644 index 00000000..6021a5d6 --- /dev/null +++ b/05/orders.php @@ -0,0 +1,24 @@ + + +
+

Orders

+ + +

No orders yet.

+ + + + + +

+ Back to Order Form +

+
+ + diff --git a/05/orders.sql b/05/orders.sql new file mode 100644 index 00000000..9c3c322d --- /dev/null +++ b/05/orders.sql @@ -0,0 +1,10 @@ +CREATE TABLE orders ( + id INT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone INT(100) NOT NULL, + address VARCHAR(100) NOT NULL, + email VARCHAR(150) NOT NULL, + comments VARCHAR(200) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); diff --git a/05/process.php b/05/process.php new file mode 100644 index 00000000..3707479f --- /dev/null +++ b/05/process.php @@ -0,0 +1,103 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; + echo "
"; + echo "

Please fix the following:

"; + echo ""; + echo "
"; + + require "includes/footer.php"; + exit; +} + +/*4*/ + +//build our query using named placeholders + +$sql = "INSERT INTO orders (first_name, last_name, phone, address, email, comments) VALUES (:first_name, :last_name, :phone, :address, :email, :comments)"; + +//prepare the query + +$stmt = $pdo->prepare($sql); + +//map the named placeholder to the user data/actual data + +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':comments', $comments); + +//execute the query +$stmt->execute(); + +//close the connection +$pdo = null; +?> + +
+

Thank you for your order, !

+

+ We’ve received your order and will contact you at + . +

+
+ + diff --git a/05/styles/main.css b/05/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/05/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/06/01- create-simplified/assets/bitumi.png b/06/01- create-simplified/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/06/01- create-simplified/assets/bitumi.png differ diff --git a/06/01- create-simplified/includes/connect.php b/06/01- create-simplified/includes/connect.php new file mode 100644 index 00000000..bc9a2c3f --- /dev/null +++ b/06/01- create-simplified/includes/connect.php @@ -0,0 +1,18 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/06/01- create-simplified/includes/footer.php b/06/01- create-simplified/includes/footer.php new file mode 100644 index 00000000..29d99ae4 --- /dev/null +++ b/06/01- create-simplified/includes/footer.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/06/01- create-simplified/includes/header.php b/06/01- create-simplified/includes/header.php new file mode 100644 index 00000000..1bf3d149 --- /dev/null +++ b/06/01- create-simplified/includes/header.php @@ -0,0 +1,27 @@ + + + + + + + Week 6 - CRUD + + + + + + +
+

+ +

+ +
\ No newline at end of file diff --git a/06/01- create-simplified/index.php b/06/01- create-simplified/index.php new file mode 100644 index 00000000..5827f263 --- /dev/null +++ b/06/01- create-simplified/index.php @@ -0,0 +1,38 @@ + +
+

Order Online - Easy & Simple (And Totally Secure...) 🧁

+
+ + + +
+ Customer Information + + + + + + + + + + +
+ +
+ Additional Comments + +
+ +
+ + + +
+
+ + + + + \ No newline at end of file diff --git a/06/01- create-simplified/orders.sql b/06/01- create-simplified/orders.sql new file mode 100644 index 00000000..96e22712 --- /dev/null +++ b/06/01- create-simplified/orders.sql @@ -0,0 +1,10 @@ +CREATE TABLE orders ( + id INT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone INT(100) NOT NULL, + address VARCHAR(100) NOT NULL, + email VARCHAR(150) NOT NULL, + comments VARCHAR(200) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/06/01- create-simplified/process.php b/06/01- create-simplified/process.php new file mode 100644 index 00000000..69fb0b33 --- /dev/null +++ b/06/01- create-simplified/process.php @@ -0,0 +1,90 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +//require address +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +//loop through error messages +//if there are errors, display to user and exit the script +if (!empty($errors)) { + foreach ($errors as $error) : ?> +
  • +prepare($sql); + +//bind parameters +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +//execute the query, matching the placeholder with the data entered by user +$stmt->execute(); + +//close connection +$pdo = null; +?> + +
    + + Thanks for your order " . $firstName . ""; ?> +
    + + \ No newline at end of file diff --git a/06/01- create-simplified/styles/main.css b/06/01- create-simplified/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/06/01- create-simplified/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/06/02-create-read-full-form/assets/bitumi.png b/06/02-create-read-full-form/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/06/02-create-read-full-form/assets/bitumi.png differ diff --git a/06/02-create-read-full-form/includes/connect.php b/06/02-create-read-full-form/includes/connect.php new file mode 100644 index 00000000..bc9a2c3f --- /dev/null +++ b/06/02-create-read-full-form/includes/connect.php @@ -0,0 +1,18 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/06/02-create-read-full-form/includes/footer.php b/06/02-create-read-full-form/includes/footer.php new file mode 100644 index 00000000..29d99ae4 --- /dev/null +++ b/06/02-create-read-full-form/includes/footer.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/06/02-create-read-full-form/includes/header.php b/06/02-create-read-full-form/includes/header.php new file mode 100644 index 00000000..2d5524d0 --- /dev/null +++ b/06/02-create-read-full-form/includes/header.php @@ -0,0 +1,26 @@ + + + + + + + Week 6 - CRUD + + + + + +
    +

    + +

    + +
    \ No newline at end of file diff --git a/06/02-create-read-full-form/index.php b/06/02-create-read-full-form/index.php new file mode 100644 index 00000000..3d60e295 --- /dev/null +++ b/06/02-create-read-full-form/index.php @@ -0,0 +1,90 @@ + +
    +

    Order Online - Easy & Simple (And Totally Secure...) 🧁

    +
    + + + +
    + Customer Information + + + + + + + + + + +
    + + +
    + Order Details + +

    + Enter a quantity for each item (use 0 if you don't want it). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Baked TreatQuantity
    Chaos Croissant 🥐 + + +
    Existential Éclair 🤔 + + +
    Procrastination Cookie ⏰ + + +
    + +
    + +
    + Additional Comments + +

    + + +

    +
    + +

    + +

    + +
    +
    + + + + + \ No newline at end of file diff --git a/06/02-create-read-full-form/orders.php b/06/02-create-read-full-form/orders.php new file mode 100644 index 00000000..cfe1dd83 --- /dev/null +++ b/06/02-create-read-full-form/orders.php @@ -0,0 +1,68 @@ +prepare($sql); + +//execute +$stmt->execute(); + +//retrieve all rows returned by a SQL query at once +$orders = $stmt->fetchAll(); +?> + +
    +

    Orders

    + + +

    No orders yet.

    + + + + +

    + Back to Order Form +

    +
    + + diff --git a/06/02-create-read-full-form/orders1.sql b/06/02-create-read-full-form/orders1.sql new file mode 100644 index 00000000..1583d244 --- /dev/null +++ b/06/02-create-read-full-form/orders1.sql @@ -0,0 +1,13 @@ +CREATE TABLE orders1 ( + customer_id INT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone VARCHAR(20) NOT NULL, + address VARCHAR(255) NOT NULL, + email VARCHAR(150) NOT NULL, + chaos_croissant INT NOT NULL DEFAULT 0, + existential_eclair INT NOT NULL DEFAULT 0, + procrastination_cookie INT NOT NULL DEFAULT 0, + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/06/02-create-read-full-form/process.php b/06/02-create-read-full-form/process.php new file mode 100644 index 00000000..03478717 --- /dev/null +++ b/06/02-create-read-full-form/process.php @@ -0,0 +1,174 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// Validate order quantities +// We only accept items with an integer quantity > 0. +$itemsOrdered = []; + +foreach ($items as $item => $quantity) { + // FILTER_VALIDATE_INT returns false if not a valid integer string + if (filter_var($quantity, FILTER_VALIDATE_INT) !== false && $quantity > 0) { + $itemsOrdered[$item] = $quantity; + } +} + +// Require at least one item to be ordered +if (count($itemsOrdered) === 0) { + $errors[] = "Please order at least one item."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; + echo "
    "; + echo "

    Please fix the following:

    "; + echo ""; + echo "
    "; + + require "includes/footer.php"; + exit; +} + +// -------------------------------------------------- +// 4. Set Up Query & Prepare +// -------------------------------------------------- +// NOTE: We insert ALL item columns every time. +// If an item was not ordered, we store 0 for that column. + +$sql = "INSERT INTO orders1 (first_name, last_name, phone, address, email, chaos_croissant, existential_eclair,procrastination_cookie, comments) VALUES (:first_name, :last_name, :phone, :address, :email, :chaos_croissant, :existential_eclair,:procrastination_cookie, :comments)"; + +$stmt = $pdo->prepare($sql); + +// -------------------------------------------------- +// 5. Bind parameters +// -------------------------------------------------- +/* + * - bindParam() binds variables by reference (value is read at execute time) + * - binding array elements directly with bindParam() can be unreliable + * because array offsets aren't always safe references. + * + * Solution: + * - Assign values to variables first, then bind those variables. + * - Use defaults (0) if an item wasn't included. + */ + +// Build “clean” values for each DB column using defaults. +// We pull from $itemsOrdered so only validated quantities get used. +$chaosCroissant = $itemsOrdered['chaos_croissant'] ?? 0; +$existentialEclair = $itemsOrdered['existential_eclair'] ?? 0; +$procrastinationCookie = $itemsOrdered['procrastination_cookie'] ?? 0; + +// bind parameters +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +// order items +// We bind as integers so the DB receives numeric values (0, 1, 2, ...). +$stmt->bindParam(':chaos_croissant', $chaosCroissant, PDO::PARAM_INT); +$stmt->bindParam(':existential_eclair', $existentialEclair, PDO::PARAM_INT); +$stmt->bindParam(':procrastination_cookie', $procrastinationCookie, PDO::PARAM_INT); + + +// -------------------------------------------------- +// 6. Execute +// -------------------------------------------------- +$stmt->execute(); + +// close the connection + +$pdo = null; + +// -------------------------------------------------- +// 7. Confirmation output +// -------------------------------------------------- + +?> + +
    +

    Thank you for your order, !

    +

    + We’ve received your order and will contact you at + . +

    +
    + + \ No newline at end of file diff --git a/06/02-create-read-full-form/styles/main.css b/06/02-create-read-full-form/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/06/02-create-read-full-form/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/06/03-update/assets/bitumi.png b/06/03-update/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/06/03-update/assets/bitumi.png differ diff --git a/06/03-update/includes/connect.php b/06/03-update/includes/connect.php new file mode 100644 index 00000000..bc9a2c3f --- /dev/null +++ b/06/03-update/includes/connect.php @@ -0,0 +1,18 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/06/03-update/includes/footer.php b/06/03-update/includes/footer.php new file mode 100644 index 00000000..29d99ae4 --- /dev/null +++ b/06/03-update/includes/footer.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/06/03-update/includes/header.php b/06/03-update/includes/header.php new file mode 100644 index 00000000..2d5524d0 --- /dev/null +++ b/06/03-update/includes/header.php @@ -0,0 +1,26 @@ + + + + + + + Week 6 - CRUD + + + + + +
    +

    + +

    + +
    \ No newline at end of file diff --git a/06/03-update/index.php b/06/03-update/index.php new file mode 100644 index 00000000..3d60e295 --- /dev/null +++ b/06/03-update/index.php @@ -0,0 +1,90 @@ + +
    +

    Order Online - Easy & Simple (And Totally Secure...) 🧁

    +
    + + + +
    + Customer Information + + + + + + + + + + +
    + + +
    + Order Details + +

    + Enter a quantity for each item (use 0 if you don't want it). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Baked TreatQuantity
    Chaos Croissant 🥐 + + +
    Existential Éclair 🤔 + + +
    Procrastination Cookie ⏰ + + +
    + +
    + +
    + Additional Comments + +

    + + +

    +
    + +

    + +

    + +
    +
    + + + + + \ No newline at end of file diff --git a/06/03-update/orders.php b/06/03-update/orders.php new file mode 100644 index 00000000..e1e67342 --- /dev/null +++ b/06/03-update/orders.php @@ -0,0 +1,100 @@ +prepare($sql); +$stmt->execute(); +$orders = $stmt->fetchAll(); +?> + +
    +

    Orders (Admin)

    + + +

    No orders yet.

    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Order #CustomerPhoneAddressEmailChaos CroissantExistential ÉclairProcrastination CookieTotal ItemsCreatedActions
    + + + + + + Update + +
    +
    + + + +

    + Back to Order Form +

    +
    + + diff --git a/06/03-update/orders1.sql b/06/03-update/orders1.sql new file mode 100644 index 00000000..22594002 --- /dev/null +++ b/06/03-update/orders1.sql @@ -0,0 +1,16 @@ +CREATE TABLE orders ( + customer_id INT AUTO_INCREMENT PRIMARY KEY, + + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone VARCHAR(20) NOT NULL, + address VARCHAR(255) NOT NULL, + email VARCHAR(150) NOT NULL, + + chaos_croissant INT NOT NULL DEFAULT 0, + existential_eclair INT NOT NULL DEFAULT 0, + procrastination_cookie INT NOT NULL DEFAULT 0, + + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/06/03-update/process.php b/06/03-update/process.php new file mode 100644 index 00000000..11706463 --- /dev/null +++ b/06/03-update/process.php @@ -0,0 +1,199 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// Validate order quantities +// We only accept items with an integer quantity > 0. +$itemsOrdered = []; + +foreach ($items as $item => $quantity) { + // FILTER_VALIDATE_INT returns false if not a valid integer string + if (filter_var($quantity, FILTER_VALIDATE_INT) !== false && $quantity > 0) { + $itemsOrdered[$item] = $quantity; + } +} + +// Require at least one item to be ordered +if (count($itemsOrdered) === 0) { + $errors[] = "Please order at least one item."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; // typically outputs DOCTYPE/head/nav/opening body tags + echo "
    "; + echo "

    Please fix the following:

    "; + echo ""; + echo "
    "; + + require "includes/footer.php"; + exit; +} + +// -------------------------------------------------- +// 4. Prepare SQL +// -------------------------------------------------- +// NOTE: We insert ALL item columns every time. +// If an item was not ordered, we store 0 for that column. +$sql = " + INSERT INTO orders1 ( + first_name, + last_name, + phone, + address, + email, + chaos_croissant, + existential_eclair, + procrastination_cookie, + comments + ) VALUES ( + :first_name, + :last_name, + :phone, + :address, + :email, + :chaos_croissant, + :existential_eclair, + :procrastination_cookie, + :comments + ) +"; + +$stmt = $pdo->prepare($sql); + +// -------------------------------------------------- +// 5. Bind parameters (improved + instructor notes) +// -------------------------------------------------- +/** + * Important teaching point: + * - bindParam() binds variables by reference (value is read at execute time) + * - binding array elements directly with bindParam() can be unreliable + * because array offsets aren't always safe references. + * + * Solution: + * - Assign values to variables first, then bind those variables. + * - Use defaults (0) if an item wasn't included. + */ + +// Build “clean” values for each DB column using defaults. +// We pull from $itemsOrdered so only validated quantities get used. + +// Customer info (bindParam is fine because these are real variables) +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +/* how does an array work with bindParam - we need a reference! $stmt->bindParam(':comments', );*/ + +$chaosCroissant = $itemsOrdered['chaos_croissant']; +$existentialEclair = $itemsOrdered['existential_eclair'] ?? 0; +$procrastinationCookie = $itemsOrdered['procrastination_cookie'] ?? 0; + + +// Order items +// We bind as integers so the DB receives numeric values (0, 1, 2, ...). +$stmt->bindParam(':chaos_croissant', $chaosCroissant, PDO::PARAM_INT); +$stmt->bindParam(':existential_eclair', $existentialEclair, PDO::PARAM_INT); +$stmt->bindParam(':procrastination_cookie', $procrastinationCookie, PDO::PARAM_INT); + + +// -------------------------------------------------- +// 6. Execute +// -------------------------------------------------- +$stmt->execute(); + +// -------------------------------------------------- +// 7. Confirmation output +// -------------------------------------------------- +// Because header.php/footer.php usually handle the page shell, +// we only output the content here (no second DOCTYPE/HTML tags). +?> + +
    +

    Thank you for your order, !

    +

    + We’ve received your order and will contact you at + . +

    +
    + + \ No newline at end of file diff --git a/06/03-update/styles/main.css b/06/03-update/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/06/03-update/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/06/03-update/update.php b/06/03-update/update.php new file mode 100644 index 00000000..4b84eb61 --- /dev/null +++ b/06/03-update/update.php @@ -0,0 +1,188 @@ +prepare($sql); + + // Bind parameters (safe + beginner friendly) + $stmt->bindParam(':first_name', $firstName); + $stmt->bindParam(':last_name', $lastName); + $stmt->bindParam(':phone', $phone); + $stmt->bindParam(':address', $address); + $stmt->bindParam(':email', $email); + + $stmt->bindParam(':chaos_croissant', $chaosCroissant); + $stmt->bindParam(':existential_eclair', $existentialEclair); + $stmt->bindParam(':procrastination_cookie', $procrastinationCookie); + + $stmt->bindParam(':customer_id', $customerId); + + $stmt->execute(); + + // Redirect back to the orders list (prevents resubmission on refresh) + header("Location: orders.php"); + exit; + } +} + +/* ------------------------------------------- + STEP 3: Load existing order data (to echo in the form) +-------------------------------------------- */ +$sql = "SELECT * FROM orders1 WHERE customer_id = :customer_id"; +$stmt = $pdo->prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +$order = $stmt->fetch(); + +if (!$order) { + die("Order not found."); +} +?> + +
    +

    Update Order #

    + + +

    + + + +
    + +

    Customer Info

    + + + + + + + + + + + + + + + + +

    Products

    + + + + + + + + + + + + Cancel + +
    +
    + + diff --git a/06/04-delete/assets/bitumi.png b/06/04-delete/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/06/04-delete/assets/bitumi.png differ diff --git a/06/04-delete/delete.php b/06/04-delete/delete.php new file mode 100644 index 00000000..c7e967d3 --- /dev/null +++ b/06/04-delete/delete.php @@ -0,0 +1,30 @@ +prepare($sql); + +//bind +$stmt->bindParam(':customer_id', $customerId); + +//execute +$stmt->execute(); + +// Redirect back to order list +header("Location: orders.php"); +exit; \ No newline at end of file diff --git a/06/04-delete/includes/connect.php b/06/04-delete/includes/connect.php new file mode 100644 index 00000000..bc9a2c3f --- /dev/null +++ b/06/04-delete/includes/connect.php @@ -0,0 +1,18 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/06/04-delete/includes/footer.php b/06/04-delete/includes/footer.php new file mode 100644 index 00000000..29d99ae4 --- /dev/null +++ b/06/04-delete/includes/footer.php @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/06/04-delete/includes/header.php b/06/04-delete/includes/header.php new file mode 100644 index 00000000..2d5524d0 --- /dev/null +++ b/06/04-delete/includes/header.php @@ -0,0 +1,26 @@ + + + + + + + Week 6 - CRUD + + + + + +
    +

    + +

    + +
    \ No newline at end of file diff --git a/06/04-delete/index.php b/06/04-delete/index.php new file mode 100644 index 00000000..3d60e295 --- /dev/null +++ b/06/04-delete/index.php @@ -0,0 +1,90 @@ + +
    +

    Order Online - Easy & Simple (And Totally Secure...) 🧁

    +
    + + + +
    + Customer Information + + + + + + + + + + +
    + + +
    + Order Details + +

    + Enter a quantity for each item (use 0 if you don't want it). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Baked TreatQuantity
    Chaos Croissant 🥐 + + +
    Existential Éclair 🤔 + + +
    Procrastination Cookie ⏰ + + +
    + +
    + +
    + Additional Comments + +

    + + +

    +
    + +

    + +

    + +
    +
    + + + + + \ No newline at end of file diff --git a/06/04-delete/orders.php b/06/04-delete/orders.php new file mode 100644 index 00000000..1c3a228e --- /dev/null +++ b/06/04-delete/orders.php @@ -0,0 +1,103 @@ +prepare($sql); +$stmt->execute(); +$orders = $stmt->fetchAll(); +?> + +
    +

    Orders (Admin)

    + + +

    No orders yet.

    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Order #CustomerPhoneAddressEmailChaos CroissantExistential ÉclairProcrastination CookieTotal ItemsCreatedActions
    + + + + + + Update + + + Delete + +
    +
    + + + Back to Order Form +
    + + \ No newline at end of file diff --git a/06/04-delete/orders1.sql b/06/04-delete/orders1.sql new file mode 100644 index 00000000..22594002 --- /dev/null +++ b/06/04-delete/orders1.sql @@ -0,0 +1,16 @@ +CREATE TABLE orders ( + customer_id INT AUTO_INCREMENT PRIMARY KEY, + + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone VARCHAR(20) NOT NULL, + address VARCHAR(255) NOT NULL, + email VARCHAR(150) NOT NULL, + + chaos_croissant INT NOT NULL DEFAULT 0, + existential_eclair INT NOT NULL DEFAULT 0, + procrastination_cookie INT NOT NULL DEFAULT 0, + + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/06/04-delete/process.php b/06/04-delete/process.php new file mode 100644 index 00000000..11706463 --- /dev/null +++ b/06/04-delete/process.php @@ -0,0 +1,199 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// Validate order quantities +// We only accept items with an integer quantity > 0. +$itemsOrdered = []; + +foreach ($items as $item => $quantity) { + // FILTER_VALIDATE_INT returns false if not a valid integer string + if (filter_var($quantity, FILTER_VALIDATE_INT) !== false && $quantity > 0) { + $itemsOrdered[$item] = $quantity; + } +} + +// Require at least one item to be ordered +if (count($itemsOrdered) === 0) { + $errors[] = "Please order at least one item."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; // typically outputs DOCTYPE/head/nav/opening body tags + echo "
    "; + echo "

    Please fix the following:

    "; + echo ""; + echo "
    "; + + require "includes/footer.php"; + exit; +} + +// -------------------------------------------------- +// 4. Prepare SQL +// -------------------------------------------------- +// NOTE: We insert ALL item columns every time. +// If an item was not ordered, we store 0 for that column. +$sql = " + INSERT INTO orders1 ( + first_name, + last_name, + phone, + address, + email, + chaos_croissant, + existential_eclair, + procrastination_cookie, + comments + ) VALUES ( + :first_name, + :last_name, + :phone, + :address, + :email, + :chaos_croissant, + :existential_eclair, + :procrastination_cookie, + :comments + ) +"; + +$stmt = $pdo->prepare($sql); + +// -------------------------------------------------- +// 5. Bind parameters (improved + instructor notes) +// -------------------------------------------------- +/** + * Important teaching point: + * - bindParam() binds variables by reference (value is read at execute time) + * - binding array elements directly with bindParam() can be unreliable + * because array offsets aren't always safe references. + * + * Solution: + * - Assign values to variables first, then bind those variables. + * - Use defaults (0) if an item wasn't included. + */ + +// Build “clean” values for each DB column using defaults. +// We pull from $itemsOrdered so only validated quantities get used. + +// Customer info (bindParam is fine because these are real variables) +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +/* how does an array work with bindParam - we need a reference! $stmt->bindParam(':comments', );*/ + +$chaosCroissant = $itemsOrdered['chaos_croissant']; +$existentialEclair = $itemsOrdered['existential_eclair'] ?? 0; +$procrastinationCookie = $itemsOrdered['procrastination_cookie'] ?? 0; + + +// Order items +// We bind as integers so the DB receives numeric values (0, 1, 2, ...). +$stmt->bindParam(':chaos_croissant', $chaosCroissant, PDO::PARAM_INT); +$stmt->bindParam(':existential_eclair', $existentialEclair, PDO::PARAM_INT); +$stmt->bindParam(':procrastination_cookie', $procrastinationCookie, PDO::PARAM_INT); + + +// -------------------------------------------------- +// 6. Execute +// -------------------------------------------------- +$stmt->execute(); + +// -------------------------------------------------- +// 7. Confirmation output +// -------------------------------------------------- +// Because header.php/footer.php usually handle the page shell, +// we only output the content here (no second DOCTYPE/HTML tags). +?> + +
    +

    Thank you for your order, !

    +

    + We’ve received your order and will contact you at + . +

    +
    + + \ No newline at end of file diff --git a/06/04-delete/styles/main.css b/06/04-delete/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/06/04-delete/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/06/04-delete/update.php b/06/04-delete/update.php new file mode 100644 index 00000000..4b84eb61 --- /dev/null +++ b/06/04-delete/update.php @@ -0,0 +1,188 @@ +prepare($sql); + + // Bind parameters (safe + beginner friendly) + $stmt->bindParam(':first_name', $firstName); + $stmt->bindParam(':last_name', $lastName); + $stmt->bindParam(':phone', $phone); + $stmt->bindParam(':address', $address); + $stmt->bindParam(':email', $email); + + $stmt->bindParam(':chaos_croissant', $chaosCroissant); + $stmt->bindParam(':existential_eclair', $existentialEclair); + $stmt->bindParam(':procrastination_cookie', $procrastinationCookie); + + $stmt->bindParam(':customer_id', $customerId); + + $stmt->execute(); + + // Redirect back to the orders list (prevents resubmission on refresh) + header("Location: orders.php"); + exit; + } +} + +/* ------------------------------------------- + STEP 3: Load existing order data (to echo in the form) +-------------------------------------------- */ +$sql = "SELECT * FROM orders1 WHERE customer_id = :customer_id"; +$stmt = $pdo->prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +$order = $stmt->fetch(); + +if (!$order) { + die("Order not found."); +} +?> + +
    +

    Update Order #

    + + +

    + + + +
    + +

    Customer Info

    + + + + + + + + + + + + + + + + +

    Products

    + + + + + + + + + + + + Cancel + +
    +
    + + diff --git a/09/assets/bitumi.png b/09/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/09/assets/bitumi.png differ diff --git a/09/delete.php b/09/delete.php new file mode 100644 index 00000000..fefd829f --- /dev/null +++ b/09/delete.php @@ -0,0 +1,29 @@ +prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +// Redirect back to admin list +header("Location: orders.php"); +exit; diff --git a/09/includes/auth.php b/09/includes/auth.php new file mode 100644 index 00000000..2bc53565 --- /dev/null +++ b/09/includes/auth.php @@ -0,0 +1,15 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/09/includes/footer.php b/09/includes/footer.php new file mode 100644 index 00000000..c670c53c --- /dev/null +++ b/09/includes/footer.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/09/includes/header.php b/09/includes/header.php new file mode 100644 index 00000000..ed3ee171 --- /dev/null +++ b/09/includes/header.php @@ -0,0 +1,70 @@ + + + + + + + Bake It Til You Make It! + + + + + +
    +

    + +

    + + +
    \ No newline at end of file diff --git a/09/includes/header_admin.php b/09/includes/header_admin.php new file mode 100644 index 00000000..a787d56a --- /dev/null +++ b/09/includes/header_admin.php @@ -0,0 +1,70 @@ + + + + + + + Bake It Til You Make It! + + + + + +
    +

    + +

    + + +
    \ No newline at end of file diff --git a/09/index.php b/09/index.php new file mode 100644 index 00000000..3495fc5f --- /dev/null +++ b/09/index.php @@ -0,0 +1,90 @@ + +
    +

    Order Online - Easy & Simple (And Totally Secure...) 🧁

    +
    + + + +
    + Customer Information + + + + + + + + + + +
    + + +
    + Order Details + +

    + Enter a quantity for each item (use 0 if you don't want it). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Baked TreatQuantity
    Chaos Croissant 🥐 + + +
    Existential Éclair 🤔 + + +
    Procrastination Cookie ⏰ + + +
    + +
    + +
    + Additional Comments + +

    + + +

    +
    + +

    + +

    + +
    +
    + + + + + \ No newline at end of file diff --git a/09/login.php b/09/login.php new file mode 100644 index 00000000..9da80500 --- /dev/null +++ b/09/login.php @@ -0,0 +1,120 @@ +prepare($sql); + + // Bind the user input to the :login parameter + $stmt->bindParam(':login', $usernameOrEmail); + + // Execute the query + $stmt->execute(); + + // Fetch the matching user as an associative array + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check two conditions: + // 1. A user record was found + // 2. The entered password matches the stored hashed password + if ($user && password_verify($password, $user['password'])) { + + // Regenerate the session ID for security + // This helps prevent session fixation attacks + session_regenerate_id(true); + + // Store user information in session variables + // These variables indicate the user is now logged in + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + + // Redirect the user to the protected orders page + header("Location: orders.php"); + + // Stop the script immediately after redirecting + exit; + + } else { + + // If login fails, display an error message + $error = "Invalid credentials. Please try again!"; + } + } +} +?> + +
    +

    Login

    + + + +
    + + +
    + + + +
    + + + + + + + + + + + + + + Create Account +
    +
    + + \ No newline at end of file diff --git a/09/logout.php b/09/logout.php new file mode 100644 index 00000000..b65cfcfb --- /dev/null +++ b/09/logout.php @@ -0,0 +1,23 @@ +prepare($sql); +$stmt->execute(); +$orders = $stmt->fetchAll(); +?> + +
    +

    Orders (Admin)

    + + +

    No orders yet.

    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Order #CustomerPhoneAddressEmailChaos CroissantExistential ÉclairProcrastination CookieTotal ItemsCreatedActions
    + + + + + + Update + + + Delete + +
    +
    + + + Back to Order Form +
    + + + + \ No newline at end of file diff --git a/09/process.php b/09/process.php new file mode 100644 index 00000000..11706463 --- /dev/null +++ b/09/process.php @@ -0,0 +1,199 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// Validate order quantities +// We only accept items with an integer quantity > 0. +$itemsOrdered = []; + +foreach ($items as $item => $quantity) { + // FILTER_VALIDATE_INT returns false if not a valid integer string + if (filter_var($quantity, FILTER_VALIDATE_INT) !== false && $quantity > 0) { + $itemsOrdered[$item] = $quantity; + } +} + +// Require at least one item to be ordered +if (count($itemsOrdered) === 0) { + $errors[] = "Please order at least one item."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; // typically outputs DOCTYPE/head/nav/opening body tags + echo "
    "; + echo "

    Please fix the following:

    "; + echo ""; + echo "
    "; + + require "includes/footer.php"; + exit; +} + +// -------------------------------------------------- +// 4. Prepare SQL +// -------------------------------------------------- +// NOTE: We insert ALL item columns every time. +// If an item was not ordered, we store 0 for that column. +$sql = " + INSERT INTO orders1 ( + first_name, + last_name, + phone, + address, + email, + chaos_croissant, + existential_eclair, + procrastination_cookie, + comments + ) VALUES ( + :first_name, + :last_name, + :phone, + :address, + :email, + :chaos_croissant, + :existential_eclair, + :procrastination_cookie, + :comments + ) +"; + +$stmt = $pdo->prepare($sql); + +// -------------------------------------------------- +// 5. Bind parameters (improved + instructor notes) +// -------------------------------------------------- +/** + * Important teaching point: + * - bindParam() binds variables by reference (value is read at execute time) + * - binding array elements directly with bindParam() can be unreliable + * because array offsets aren't always safe references. + * + * Solution: + * - Assign values to variables first, then bind those variables. + * - Use defaults (0) if an item wasn't included. + */ + +// Build “clean” values for each DB column using defaults. +// We pull from $itemsOrdered so only validated quantities get used. + +// Customer info (bindParam is fine because these are real variables) +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +/* how does an array work with bindParam - we need a reference! $stmt->bindParam(':comments', );*/ + +$chaosCroissant = $itemsOrdered['chaos_croissant']; +$existentialEclair = $itemsOrdered['existential_eclair'] ?? 0; +$procrastinationCookie = $itemsOrdered['procrastination_cookie'] ?? 0; + + +// Order items +// We bind as integers so the DB receives numeric values (0, 1, 2, ...). +$stmt->bindParam(':chaos_croissant', $chaosCroissant, PDO::PARAM_INT); +$stmt->bindParam(':existential_eclair', $existentialEclair, PDO::PARAM_INT); +$stmt->bindParam(':procrastination_cookie', $procrastinationCookie, PDO::PARAM_INT); + + +// -------------------------------------------------- +// 6. Execute +// -------------------------------------------------- +$stmt->execute(); + +// -------------------------------------------------- +// 7. Confirmation output +// -------------------------------------------------- +// Because header.php/footer.php usually handle the page shell, +// we only output the content here (no second DOCTYPE/HTML tags). +?> + +
    +

    Thank you for your order, !

    +

    + We’ve received your order and will contact you at + . +

    +
    + + \ No newline at end of file diff --git a/09/register.php b/09/register.php new file mode 100644 index 00000000..de62d755 --- /dev/null +++ b/09/register.php @@ -0,0 +1,197 @@ +prepare($sql); + + // Bind user inputs to the query parameters + $stmt->bindParam(':username', $username); + $stmt->bindParam(':email', $email); + + // Execute the query + $stmt->execute(); + + // If a record is returned, the username or email is already in use + if ($stmt->fetch()) { + $errors[] = "That username or email has already been used!"; + } + } + // -------------------------------------------------- + // Insert the new user into the database + // -------------------------------------------------- + + // Only insert if there are still no errors + if (empty($errors)) { + // Hash the password before storing it in the database + // This ensures passwords are not stored in plain text + $hashedPassword = password_hash($password, PASSWORD_DEFAULT); + // SQL query to insert the new user + $sql = "INSERT INTO users (username, email, password) VALUES (:username, :email, :password)"; + // Prepare the insert statement + $stmt = $pdo->prepare($sql); + + // Bind the values to the query parameters + $stmt->bindParam(':username', $username); + $stmt->bindParam(':email', $email); + $stmt->bindParam(':password', $hashedPassword); + + // Execute the insert query + $stmt->execute(); + // Set a success message + $success = "Account create successfully. You can now login!"; + } +} +?> + +
    +

    Sign Up

    + + + +
    +

    Please fix the following:

    + +
    + + + + +
    + +
    + + Go to Login +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + Login Instead +
    +
    + + \ No newline at end of file diff --git a/09/restricted.php b/09/restricted.php new file mode 100644 index 00000000..bd0231cb --- /dev/null +++ b/09/restricted.php @@ -0,0 +1,11 @@ + + + +
    +

    Sorry, you must be logged into view this content!

    + Back to Home Page +
    + + + + \ No newline at end of file diff --git a/09/sql/orders1.sql b/09/sql/orders1.sql new file mode 100644 index 00000000..22594002 --- /dev/null +++ b/09/sql/orders1.sql @@ -0,0 +1,16 @@ +CREATE TABLE orders ( + customer_id INT AUTO_INCREMENT PRIMARY KEY, + + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone VARCHAR(20) NOT NULL, + address VARCHAR(255) NOT NULL, + email VARCHAR(150) NOT NULL, + + chaos_croissant INT NOT NULL DEFAULT 0, + existential_eclair INT NOT NULL DEFAULT 0, + procrastination_cookie INT NOT NULL DEFAULT 0, + + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/09/sql/users.sql b/09/sql/users.sql new file mode 100644 index 00000000..2f0a1e57 --- /dev/null +++ b/09/sql/users.sql @@ -0,0 +1,7 @@ +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL, + email VARCHAR(100) NOT NULL, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/09/styles/main.css b/09/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/09/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/09/update.php b/09/update.php new file mode 100644 index 00000000..3f79e572 --- /dev/null +++ b/09/update.php @@ -0,0 +1,189 @@ +prepare($sql); + + // Bind parameters (safe + beginner friendly) + $stmt->bindParam(':first_name', $firstName); + $stmt->bindParam(':last_name', $lastName); + $stmt->bindParam(':phone', $phone); + $stmt->bindParam(':address', $address); + $stmt->bindParam(':email', $email); + + $stmt->bindParam(':chaos_croissant', $chaosCroissant); + $stmt->bindParam(':existential_eclair', $existentialEclair); + $stmt->bindParam(':procrastination_cookie', $procrastinationCookie); + + $stmt->bindParam(':customer_id', $customerId); + + $stmt->execute(); + + // Redirect back to the orders list (prevents resubmission on refresh) + header("Location: orders.php"); + exit; + } +} + +/* ------------------------------------------- + STEP 3: Load existing order data (to echo in the form) +-------------------------------------------- */ +$sql = "SELECT * FROM orders1 WHERE customer_id = :customer_id"; +$stmt = $pdo->prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +$order = $stmt->fetch(); + +if (!$order) { + die("Order not found."); +} +?> + +
    +

    Update Order #

    + + +

    + + + +
    + +

    Customer Info

    + + + + + + + + + + + + + + + + +

    Products

    + + + + + + + + + + + + Cancel + +
    +
    + + diff --git a/10/add-product.php b/10/add-product.php new file mode 100644 index 00000000..8668cde3 --- /dev/null +++ b/10/add-product.php @@ -0,0 +1,157 @@ +prepare($sql); + $stmt->bindParam(':name', $name); + $stmt->bindParam(':description', $description); + $stmt->bindParam(':price', $price); + $stmt->bindParam(':image_path', $imagePath); + $stmt->execute(); + + $success = "Product added successfully!"; + } +} +?> + +
    +

    Add Product

    + + +
    +

    Please fix the following:

    + +
    + + + +
    + +
    + + +
    + + + + + + + + + + + + + +
    +
    + + \ No newline at end of file diff --git a/10/assets/bitumi.png b/10/assets/bitumi.png new file mode 100644 index 00000000..bb7fe530 Binary files /dev/null and b/10/assets/bitumi.png differ diff --git a/10/delete.php b/10/delete.php new file mode 100644 index 00000000..fefd829f --- /dev/null +++ b/10/delete.php @@ -0,0 +1,29 @@ +prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +// Redirect back to admin list +header("Location: orders.php"); +exit; diff --git a/10/includes/auth.php b/10/includes/auth.php new file mode 100644 index 00000000..1e40ddcd --- /dev/null +++ b/10/includes/auth.php @@ -0,0 +1,16 @@ +setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION); +} +//what happens if there is an error connecting +catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} diff --git a/10/includes/footer.php b/10/includes/footer.php new file mode 100644 index 00000000..c670c53c --- /dev/null +++ b/10/includes/footer.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/10/includes/header.php b/10/includes/header.php new file mode 100644 index 00000000..07c84b43 --- /dev/null +++ b/10/includes/header.php @@ -0,0 +1,70 @@ + + + + + + + Bake It Til You Make It! + + + + + +
    +

    + +

    + + +
    \ No newline at end of file diff --git a/10/includes/header_admin.php b/10/includes/header_admin.php new file mode 100644 index 00000000..cc99c9f9 --- /dev/null +++ b/10/includes/header_admin.php @@ -0,0 +1,67 @@ + + + + + + + Bake It Til You Make It! + + + + + +
    +

    + +

    + + +
    \ No newline at end of file diff --git a/10/index.php b/10/index.php new file mode 100644 index 00000000..3495fc5f --- /dev/null +++ b/10/index.php @@ -0,0 +1,90 @@ + +
    +

    Order Online - Easy & Simple (And Totally Secure...) 🧁

    +
    + + + +
    + Customer Information + + + + + + + + + + +
    + + +
    + Order Details + +

    + Enter a quantity for each item (use 0 if you don't want it). +

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Baked TreatQuantity
    Chaos Croissant 🥐 + + +
    Existential Éclair 🤔 + + +
    Procrastination Cookie ⏰ + + +
    + +
    + +
    + Additional Comments + +

    + + +

    +
    + +

    + +

    + +
    +
    + + + + + \ No newline at end of file diff --git a/10/lesson_outline.txt b/10/lesson_outline.txt new file mode 100644 index 00000000..9ac630df --- /dev/null +++ b/10/lesson_outline.txt @@ -0,0 +1,290 @@ +# Instructor Guide: Teaching Basic User Authentication in PHP + +## Overview + +This lesson introduces students to **user authentication using PHP sessions and a MySQL database**. Students will learn how to create user accounts, log in securely using password hashing, maintain login state with sessions, and restrict access to protected pages. + +The goal is to build authentication **incrementally**, introducing one new concept at a time so students understand how each component contributes to the full login system. + +--- + +# Learning Outcome + +By the end of this lesson, students should be able to: + +* Create a `users` database table for storing account data +* Build a registration form and validate user input +* Hash and store passwords securely using `password_hash()` +* Verify credentials using `password_verify()` +* Create login sessions using `$_SESSION` +* Restrict access to protected pages using an authentication check +* Destroy sessions when logging out + +--- + +# Suggested Teaching Sequence + +## 1. Create the Users Table + +Begin by explaining that applications must store user accounts in the database just like any other data. + +Key concepts: + +* primary keys +* unique constraints +* password storage + +Example SQL: + +```sql +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + email VARCHAR(100) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +``` + +Important discussion points: + +* Passwords should **never be stored in plain text** +* Hashes require longer storage fields (255 characters) +* Unique fields prevent duplicate accounts + +--- + +## 2. Create the Registration Form + +Students build a standard HTML form containing: + +* username +* email +* password +* confirm password + +Explain that forms must be validated before data is inserted into the database. + +Server-side validation should include: + +* required fields +* valid email format +* minimum password length +* matching password confirmation + +Example validation logic: + +```php +if ($password !== $confirmPassword) { + $errors[] = "Passwords do not match."; +} +``` + +--- + +## 3. Store Passwords Securely + +Introduce password hashing. + +Explain the security principle: + +> We never store passwords directly. We store a **hash** of the password. + +Example: + +```php +$passwordHash = password_hash($password, PASSWORD_DEFAULT); +``` + +Students then insert the hashed password into the database using prepared statements. + +This is a good opportunity to reinforce: + +* prepared statements +* SQL injection prevention + +--- + +## 4. Build the Login Form + +Students create a login form with: + +* email (or username) +* password + +Explain the login process conceptually: + +1. User submits credentials +2. Application retrieves the user from the database +3. Password is verified against the stored hash +4. A session is created + +--- + +## 5. Verify the Password + +Introduce the verification function: + +```php +password_verify($password, $user['password']) +``` + +Explain that this compares the entered password with the stored hash and returns **true or false**. + +This demonstrates how hashing still allows secure password comparison. + +--- + +## 6. Introduce Sessions (Login State) + +Explain the problem with HTTP: + +> HTTP is stateless — the server forgets who the user is between requests. + +Sessions solve this by storing user data on the server. + +When login succeeds: + +```php +session_start(); + +$_SESSION['id'] = $user['id']; +$_SESSION['username'] = $user['username']; + +header("Location: orders.php"); +exit(); +``` + +Key points: + +* `session_start()` must run before any HTML output +* session variables store user identity +* sessions allow users to remain logged in across pages + +--- + +## 7. Create an Authentication Check (auth.php) + +Introduce the idea of **protecting pages**. + +Create a small script that verifies whether the user is logged in. + +Example `includes/auth.php`: + +```php +prepare($sql); + $stmt->bindParam(':login', $usernameOrEmail); + $stmt->execute(); + + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user && password_verify($password, $user['password'])) { + session_regenerate_id(true); + + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + + header("Location: orders.php"); + exit; + } else { + $error = "Invalid credentials. Please try again."; + } + } +} +?> + +
    +

    Login

    + + +
    + +
    + + +
    + + + + + + + + Create Account +
    +
    + + \ No newline at end of file diff --git a/10/logout.php b/10/logout.php new file mode 100644 index 00000000..2c40bbb0 --- /dev/null +++ b/10/logout.php @@ -0,0 +1,23 @@ +prepare($sql); +$stmt->execute(); +$orders = $stmt->fetchAll(); +?> + +
    +

    Orders (Admin)

    + + +

    No orders yet.

    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Order #CustomerPhoneAddressEmailChaos CroissantExistential ÉclairProcrastination CookieTotal ItemsCreatedActions
    + + + + + + Update + + + Delete + +
    +
    + + + Back to Order Form +
    + + + + \ No newline at end of file diff --git a/10/process.php b/10/process.php new file mode 100644 index 00000000..11706463 --- /dev/null +++ b/10/process.php @@ -0,0 +1,199 @@ + ['regexp' => '/^[0-9\-\+\(\)\s]{7,25}$/'] +])) { + $errors[] = "Phone number format is invalid."; +} + +// Address: required +if ($address === null || $address === '') { + $errors[] = "Address is required."; +} + +// Validate order quantities +// We only accept items with an integer quantity > 0. +$itemsOrdered = []; + +foreach ($items as $item => $quantity) { + // FILTER_VALIDATE_INT returns false if not a valid integer string + if (filter_var($quantity, FILTER_VALIDATE_INT) !== false && $quantity > 0) { + $itemsOrdered[$item] = $quantity; + } +} + +// Require at least one item to be ordered +if (count($itemsOrdered) === 0) { + $errors[] = "Please order at least one item."; +} + +// If there are errors, show them and stop the script before inserting to the DB +if (!empty($errors)) { + require "includes/header.php"; // typically outputs DOCTYPE/head/nav/opening body tags + echo "
    "; + echo "

    Please fix the following:

    "; + echo ""; + echo "
    "; + + require "includes/footer.php"; + exit; +} + +// -------------------------------------------------- +// 4. Prepare SQL +// -------------------------------------------------- +// NOTE: We insert ALL item columns every time. +// If an item was not ordered, we store 0 for that column. +$sql = " + INSERT INTO orders1 ( + first_name, + last_name, + phone, + address, + email, + chaos_croissant, + existential_eclair, + procrastination_cookie, + comments + ) VALUES ( + :first_name, + :last_name, + :phone, + :address, + :email, + :chaos_croissant, + :existential_eclair, + :procrastination_cookie, + :comments + ) +"; + +$stmt = $pdo->prepare($sql); + +// -------------------------------------------------- +// 5. Bind parameters (improved + instructor notes) +// -------------------------------------------------- +/** + * Important teaching point: + * - bindParam() binds variables by reference (value is read at execute time) + * - binding array elements directly with bindParam() can be unreliable + * because array offsets aren't always safe references. + * + * Solution: + * - Assign values to variables first, then bind those variables. + * - Use defaults (0) if an item wasn't included. + */ + +// Build “clean” values for each DB column using defaults. +// We pull from $itemsOrdered so only validated quantities get used. + +// Customer info (bindParam is fine because these are real variables) +$stmt->bindParam(':first_name', $firstName); +$stmt->bindParam(':last_name', $lastName); +$stmt->bindParam(':phone', $phone); +$stmt->bindParam(':address', $address); +$stmt->bindParam(':email', $email); +$stmt->bindParam(':comments', $comments); + +/* how does an array work with bindParam - we need a reference! $stmt->bindParam(':comments', );*/ + +$chaosCroissant = $itemsOrdered['chaos_croissant']; +$existentialEclair = $itemsOrdered['existential_eclair'] ?? 0; +$procrastinationCookie = $itemsOrdered['procrastination_cookie'] ?? 0; + + +// Order items +// We bind as integers so the DB receives numeric values (0, 1, 2, ...). +$stmt->bindParam(':chaos_croissant', $chaosCroissant, PDO::PARAM_INT); +$stmt->bindParam(':existential_eclair', $existentialEclair, PDO::PARAM_INT); +$stmt->bindParam(':procrastination_cookie', $procrastinationCookie, PDO::PARAM_INT); + + +// -------------------------------------------------- +// 6. Execute +// -------------------------------------------------- +$stmt->execute(); + +// -------------------------------------------------- +// 7. Confirmation output +// -------------------------------------------------- +// Because header.php/footer.php usually handle the page shell, +// we only output the content here (no second DOCTYPE/HTML tags). +?> + +
    +

    Thank you for your order, !

    +

    + We’ve received your order and will contact you at + . +

    +
    + + \ No newline at end of file diff --git a/10/products.php b/10/products.php new file mode 100644 index 00000000..c279d121 --- /dev/null +++ b/10/products.php @@ -0,0 +1,20 @@ +prepare($sql); + $stmt->execute(); + $prepare=$stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + +
    +

    Our Products

    + +
    + + \ No newline at end of file diff --git a/10/register.php b/10/register.php new file mode 100644 index 00000000..47a8c7b1 --- /dev/null +++ b/10/register.php @@ -0,0 +1,207 @@ +prepare($sql); + + // Bind user inputs to the query parameters + $stmt->bindParam(':username', $username); + $stmt->bindParam(':email', $email); + + // Execute the query + $stmt->execute(); + + // If a record is returned, the username or email is already in use + if ($stmt->fetch()) { + $errors[] = "That username or email is already in use."; + } + } + + // -------------------------------------------------- + // Insert the new user into the database + // -------------------------------------------------- + + // Only insert if there are still no errors + if (empty($errors)) { + + // Hash the password before storing it in the database + // This ensures passwords are not stored in plain text + $hashedPassword = password_hash($password, PASSWORD_DEFAULT); + + // SQL query to insert the new user + $sql = "INSERT INTO users (username, email, password) + VALUES (:username, :email, :password)"; + + // Prepare the insert statement + $stmt = $pdo->prepare($sql); + + // Bind the values to the query parameters + $stmt->bindParam(':username', $username); + $stmt->bindParam(':email', $email); + $stmt->bindParam(':password', $hashedPassword); + + // Execute the insert query + $stmt->execute(); + + // Set a success message + $success = "Account created successfully. You can now log in."; + } +} +?> + +
    +

    Sign Up

    + + + +
    +

    Please fix the following:

    + +
    + + + + +
    + +
    + + Go to Login +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + Login Instead +
    +
    + + \ No newline at end of file diff --git a/10/restricted.php b/10/restricted.php new file mode 100644 index 00000000..bd0231cb --- /dev/null +++ b/10/restricted.php @@ -0,0 +1,11 @@ + + + +
    +

    Sorry, you must be logged into view this content!

    + Back to Home Page +
    + + + + \ No newline at end of file diff --git a/10/sql/orders1.sql b/10/sql/orders1.sql new file mode 100644 index 00000000..22594002 --- /dev/null +++ b/10/sql/orders1.sql @@ -0,0 +1,16 @@ +CREATE TABLE orders ( + customer_id INT AUTO_INCREMENT PRIMARY KEY, + + first_name VARCHAR(100) NOT NULL, + last_name VARCHAR(100) NOT NULL, + phone VARCHAR(20) NOT NULL, + address VARCHAR(255) NOT NULL, + email VARCHAR(150) NOT NULL, + + chaos_croissant INT NOT NULL DEFAULT 0, + existential_eclair INT NOT NULL DEFAULT 0, + procrastination_cookie INT NOT NULL DEFAULT 0, + + comments TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/10/sql/products.sql b/10/sql/products.sql new file mode 100644 index 00000000..c3857cec --- /dev/null +++ b/10/sql/products.sql @@ -0,0 +1,8 @@ +CREATE TABLE products ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(150) NOT NULL, + description TEXT NOT NULL, + price DECIMAL(10,2) NOT NULL, + image_path VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/10/sql/users.sql b/10/sql/users.sql new file mode 100644 index 00000000..2f0a1e57 --- /dev/null +++ b/10/sql/users.sql @@ -0,0 +1,7 @@ +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL, + email VARCHAR(100) NOT NULL, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/10/styles/main.css b/10/styles/main.css new file mode 100644 index 00000000..39cf0a49 --- /dev/null +++ b/10/styles/main.css @@ -0,0 +1,134 @@ +/* ============================ + Simple Reset / Base Styles + ============================ */ + +/* Box sizing reset */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margins */ +body, +h1, +h2, +h3, +p, +fieldset, +legend { + margin: 0; +} + +/* Base body styles */ +body { + line-height: 1.5; + color: #222; + background-color: #fafafa; +} + +/* ============================ + Layout + ============================ */ + +main, +header nav, +footer { + max-width: 80%; + margin: 2rem auto; + padding: 1.5rem; +} + +/* ============================ + Logo / Heading + ============================ */ + +h1 { + text-align: center; + margin-bottom: 1.5rem; +} + +h1 img { + max-width: 220px; + height: auto; +} + +/* ============================ + Form Elements + ============================ */ + +fieldset { + border: 1px solid #ddd; + padding: 1rem; + margin-bottom: 1.5rem; +} + +legend { + padding: 0 0.5rem; + font-weight: 600; +} + +label { + font-weight: 500; +} + +input, +select, +textarea, +button { + display:block; + font-family: inherit; + font-size: 1rem; +} + +input, +select, +textarea { + width: 100%; + padding: 0.4rem; + margin-top: 0.25rem; +} + +textarea { + resize: vertical; +} + +/* ============================ + Navigation + ============================ */ + +nav { + margin-bottom: 1.5rem; + background-color: #333; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + gap: 1.25rem; + justify-content: center; +} + +nav a { + text-decoration: none; + color: #fff; + font-weight: 500; + padding: 0.25rem 0; +} + +nav a:hover, +nav a:focus { + text-decoration: underline; +} + +/* ============================ + Footer + ============================ */ + + footer { + background-color: #000; + color: #FFF; + } \ No newline at end of file diff --git a/10/update.php b/10/update.php new file mode 100644 index 00000000..3f79e572 --- /dev/null +++ b/10/update.php @@ -0,0 +1,189 @@ +prepare($sql); + + // Bind parameters (safe + beginner friendly) + $stmt->bindParam(':first_name', $firstName); + $stmt->bindParam(':last_name', $lastName); + $stmt->bindParam(':phone', $phone); + $stmt->bindParam(':address', $address); + $stmt->bindParam(':email', $email); + + $stmt->bindParam(':chaos_croissant', $chaosCroissant); + $stmt->bindParam(':existential_eclair', $existentialEclair); + $stmt->bindParam(':procrastination_cookie', $procrastinationCookie); + + $stmt->bindParam(':customer_id', $customerId); + + $stmt->execute(); + + // Redirect back to the orders list (prevents resubmission on refresh) + header("Location: orders.php"); + exit; + } +} + +/* ------------------------------------------- + STEP 3: Load existing order data (to echo in the form) +-------------------------------------------- */ +$sql = "SELECT * FROM orders1 WHERE customer_id = :customer_id"; +$stmt = $pdo->prepare($sql); +$stmt->bindParam(':customer_id', $customerId); +$stmt->execute(); + +$order = $stmt->fetch(); + +if (!$order) { + die("Order not found."); +} +?> + +
    +

    Update Order #

    + + +

    + + + +
    + +

    Customer Info

    + + + + + + + + + + + + + + + + +

    Products

    + + + + + + + + + + + + Cancel + +
    +
    + + diff --git a/assignments/.DS_Store b/assignments/.DS_Store index b723e720..a0a6c081 100644 Binary files a/assignments/.DS_Store and b/assignments/.DS_Store differ diff --git a/assignments/labs/.DS_Store b/assignments/labs/.DS_Store index 1ed5283e..5e79f1e0 100644 Binary files a/assignments/labs/.DS_Store and b/assignments/labs/.DS_Store differ diff --git a/lab5/index.php b/lab5/index.php new file mode 100644 index 00000000..b890a66f --- /dev/null +++ b/lab5/index.php @@ -0,0 +1,31 @@ +/* +Name: Melika Kashef +Student Number: 200652992 +Course: COMP1006 +Lab: Lab Five - Image Upload +Date: March 2026 +Description: This program allows users to upload an image and displays it. +*/ + + + + + + + Lab-five-upload profile picture + + + +

    upload profile picture

    +
    + + + +

    + + +
    + + + + \ No newline at end of file diff --git a/lab5/upload.php b/lab5/upload.php new file mode 100644 index 00000000..8ae42e0f --- /dev/null +++ b/lab5/upload.php @@ -0,0 +1,53 @@ + + + + + + upload result + + + +

    upload result

    + + image uploaded successfully!

    "; + echo"

    file name:" .htmlspecialchars($fileName)."

    "; + echo"Uploaded Image"; + }else{ + echo"

    sorry, there is the problem.

    "; + } + }else{ + echo"

    only jpg, jpeg, png, Gif, and WEBP files are allowed.

    ;"; + } + }else{ + echo"

    no file uploaded or there is an error.

    "; + } + }else{ + echo"

    please upload an image using form.

    "; + } + + + + ?> +

    + + go back + + + \ No newline at end of file diff --git a/project-1/includes/config.php b/project-1/includes/config.php new file mode 100644 index 00000000..15e49156 --- /dev/null +++ b/project-1/includes/config.php @@ -0,0 +1,23 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, +]; + +try { + $pdo = new PDO($dsn, $user, $pass, $options); +} catch (PDOException $e) { + die("DB connection failed: " . $e->getMessage()); +} diff --git a/project-1/includes/footer.php b/project-1/includes/footer.php new file mode 100644 index 00000000..0d70add7 --- /dev/null +++ b/project-1/includes/footer.php @@ -0,0 +1,3 @@ + + + diff --git a/project-1/includes/header.php b/project-1/includes/header.php new file mode 100644 index 00000000..1e2984f5 --- /dev/null +++ b/project-1/includes/header.php @@ -0,0 +1,31 @@ + + + + + + + + + + + + <?= htmlspecialchars($pageTitle ?? 'Blog CMS') ?> + + + + + + + + + + + + + +
    diff --git a/project-1/public/create.php b/project-1/public/create.php new file mode 100644 index 00000000..988594d4 --- /dev/null +++ b/project-1/public/create.php @@ -0,0 +1,113 @@ + insert + if (!$errors) { + $sql = "INSERT INTO posts (title, post_date, body, category) VALUES (:title, :post_date, :body, :category)"; + $stmt = $pdo->prepare($sql); + $stmt->execute([ + ':title' => $title, + ':post_date' => $post_date, + ':body' => $body, + ':category' => $category + ]); + + header("Location: index.php"); + exit; + } +} +?> + + + + + + Add Post + + + +
    + +
    +

    Add New Post

    + Back +
    + + +
    +
      + +
    • + +
    +
    + + +
    +
    + + +
    Please enter a title (min 3 characters).
    +
    + +
    + + +
    Please select a date.
    +
    + +
    + + +
    Please enter a category.
    +
    + +
    + + +
    Please write at least 20 characters.
    +
    + + +
    + +
    + + + + + diff --git a/project-1/public/delete.php b/project-1/public/delete.php new file mode 100644 index 00000000..c165580a --- /dev/null +++ b/project-1/public/delete.php @@ -0,0 +1,12 @@ +prepare("DELETE FROM posts WHERE id = :id"); + $stmt->execute([':id' => $id]); +} + +header("Location: index.php"); +exit; diff --git a/project-1/public/edit.php b/project-1/public/edit.php new file mode 100644 index 00000000..94c4971d --- /dev/null +++ b/project-1/public/edit.php @@ -0,0 +1,114 @@ +prepare("SELECT * FROM posts WHERE id = :id"); +$stmt->execute([':id' => $id]); +$post = $stmt->fetch(); + +if (!$post) { + header("Location: index.php"); + exit; +} + +$errors = []; +$title = $post['title']; +$post_date = $post['post_date']; +$body = $post['body']; +$category = $post['category']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + + $title = trim($_POST['title'] ?? ''); + $post_date = trim($_POST['post_date'] ?? ''); + $body = trim($_POST['body'] ?? ''); + $category = trim($_POST['category'] ?? ''); + + // Server-side validation + if ($title === '' || strlen($title) < 3) $errors[] = "Title must be at least 3 characters."; + if ($post_date === '') $errors[] = "Date is required."; + if ($body === '' || strlen($body) < 20) $errors[] = "Body must be at least 20 characters."; + if ($category === '') $errors[] = "Category is required."; + + if (!$errors) { + $sql = "UPDATE posts + SET title = :title, + post_date = :post_date, + body = :body, + category = :category + WHERE id = :id"; + + $stmt = $pdo->prepare($sql); + $stmt->execute([ + ':title' => $title, + ':post_date' => $post_date, + ':body' => $body, + ':category' => $category, + ':id' => $id + ]); + + header("Location: index.php"); + exit; + } +} +?> + + + + + Edit Post + + + +
    + +

    Edit Post

    + + +
    +
      + +
    • + +
    +
    + + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + + + Cancel +
    + +
    + + diff --git a/project-1/public/index.php b/project-1/public/index.php new file mode 100644 index 00000000..91b7e534 --- /dev/null +++ b/project-1/public/index.php @@ -0,0 +1,82 @@ + + + +
    +

    All Posts

    + + Add New Post +
    + +query("SELECT id, title, post_date, category, created_at FROM posts ORDER BY id DESC"); + +// Convert result into array +$posts = $stmt->fetchAll(); +?> + + + +
    + No posts yet. Click “Add New Post” to create your first one. +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    TitleDateCategoryCreatedActions
    + + + Edit + + + + + Delete + +
    +
    + + + + diff --git a/week5-lab4/includes/db.php b/week5-lab4/includes/db.php new file mode 100644 index 00000000..64bf4db3 --- /dev/null +++ b/week5-lab4/includes/db.php @@ -0,0 +1,20 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, +]; + +try { + $pdo = new PDO($dsn, $username, $password, $options); +} catch (PDOException $e) { + die("Database connection failed."); +} diff --git a/week5-lab4/index.php b/week5-lab4/index.php new file mode 100644 index 00000000..63ed55b6 --- /dev/null +++ b/week5-lab4/index.php @@ -0,0 +1,25 @@ + + + + + + + Lab 4 - Subscribe + + + +

    Email Subscription

    + +
    + + + + +
    + +

    View Subscribers

    + + + diff --git a/week5-lab4/process.php b/week5-lab4/process.php new file mode 100644 index 00000000..0ef40047 --- /dev/null +++ b/week5-lab4/process.php @@ -0,0 +1,16 @@ +prepare($sql); +$stmt->execute([":email" => $email]); + +header("Location: subscribers.php"); +exit; diff --git a/week5-lab4/subscribers.php b/week5-lab4/subscribers.php new file mode 100644 index 00000000..01849a4c --- /dev/null +++ b/week5-lab4/subscribers.php @@ -0,0 +1,45 @@ +prepare($sql); +$stmt->execute(); +$subscribers = $stmt->fetchAll(); +?> + + + + + + Subscribers + + + +

    Subscriber List

    + + +

    No subscribers yet.

    + + + + + + + + + + + + + + + + +
    IDEmail
    + + +

    Back to form

    + + +