PHP Lesson 4: APIs, AJAX, Email & Building an E-commerce System
PHP Lesson 4: APIs, AJAX, Email & Building an E-commerce System
PHP Lesson 4: APIs, AJAX, Email & Building an E-commerce System
Welcome to Lesson 4! Now we're going to build professional-level PHP applications with APIs, real-time features, and payment processing.
Part 1: RESTful API Development
1.1 Building a Complete REST API
<?php
// api/config/Database.php
class Database {
private $host = 'localhost';
private $db_name = 'php_ecommerce';
private $username = 'root';
private $password = '';
public $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name,
$this->username,
$this->password
);
$this->conn->exec("set names utf8");
} catch(PDOException $exception) {
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
// api/models/Product.php
class Product {
private $conn;
private $table = 'products';
public $id;
public $name;
public $description;
public $price;
public $category_id;
public $created_at;
public function __construct($db) {
$this->conn = $db;
}
// Read all products
public function read() {
$query = "SELECT p.*, c.name as category_name
FROM " . $this->table . " p
LEFT JOIN categories c ON p.category_id = c.id
ORDER BY p.created_at DESC";
$stmt = $this->conn->prepare($query);
$stmt->execute();
return $stmt;
}
// Read single product
public function read_single() {
$query = "SELECT p.*, c.name as category_name
FROM " . $this->table . " p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.id = ? LIMIT 0,1";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(1, $this->id);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if($row) {
$this->name = $row['name'];
$this->description = $row['description'];
$this->price = $row['price'];
$this->category_id = $row['category_id'];
$this->category_name = $row['category_name'];
$this->created_at = $row['created_at'];
return true;
}
return false;
}
// Create product
public function create() {
$query = "INSERT INTO " . $this->table . "
SET name=:name, description=:description, price=:price, category_id=:category_id";
$stmt = $this->conn->prepare($query);
// Sanitize data
$this->name = htmlspecialchars(strip_tags($this->name));
$this->description = htmlspecialchars(strip_tags($this->description));
$this->price = htmlspecialchars(strip_tags($this->price));
$this->category_id = htmlspecialchars(strip_tags($this->category_id));
// Bind data
$stmt->bindParam(":name", $this->name);
$stmt->bindParam(":description", $this->description);
$stmt->bindParam(":price", $this->price);
$stmt->bindParam(":category_id", $this->category_id);
if($stmt->execute()) {
return true;
}
return false;
}
// Update product
public function update() {
$query = "UPDATE " . $this->table . "
SET name=:name, description=:description, price=:price, category_id=:category_id
WHERE id=:id";
$stmt = $this->conn->prepare($query);
// Sanitize data
$this->name = htmlspecialchars(strip_tags($this->name));
$this->description = htmlspecialchars(strip_tags($this->description));
$this->price = htmlspecialchars(strip_tags($this->price));
$this->category_id = htmlspecialchars(strip_tags($this->category_id));
$this->id = htmlspecialchars(strip_tags($this->id));
// Bind data
$stmt->bindParam(":name", $this->name);
$stmt->bindParam(":description", $this->description);
$stmt->bindParam(":price", $this->price);
$stmt->bindParam(":category_id", $this->category_id);
$stmt->bindParam(":id", $this->id);
if($stmt->execute()) {
return true;
}
return false;
}
// Delete product
public function delete() {
$query = "DELETE FROM " . $this->table . " WHERE id = ?";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(1, $this->id);
if($stmt->execute()) {
return true;
}
return false;
}
}
// api/config/Cors.php
class Cors {
public function handleCors() {
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit(0);
}
}
}
// api/products/index.php
header("Content-Type: application/json; charset=UTF-8");
require_once '../config/Database.php';
require_once '../config/Cors.php';
require_once '../models/Product.php';
$cors = new Cors();
$cors->handleCors();
$database = new Database();
$db = $database->getConnection();
$product = new Product($db);
$method = $_SERVER['REQUEST_METHOD'];
$input = json_decode(file_get_contents("php://input"), true);
$id = isset($_GET['id']) ? $_GET['id'] : null;
switch($method) {
case 'GET':
if ($id) {
$product->id = $id;
if($product->read_single()) {
http_response_code(200);
echo json_encode([
'id' => $id,
'name' => $product->name,
'description' => $product->description,
'price' => $product->price,
'category_id' => $product->category_id,
'category_name' => $product->category_name,
'created_at' => $product->created_at
]);
} else {
http_response_code(404);
echo json_encode(['message' => 'Product not found.']);
}
} else {
$stmt = $product->read();
$num = $stmt->rowCount();
if($num > 0) {
$products_arr = array();
$products_arr["data"] = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
extract($row);
$product_item = array(
"id" => $id,
"name" => $name,
"description" => $description,
"price" => $price,
"category_id" => $category_id,
"category_name" => $category_name,
"created_at" => $created_at
);
array_push($products_arr["data"], $product_item);
}
http_response_code(200);
echo json_encode($products_arr);
} else {
http_response_code(404);
echo json_encode(["message" => "No products found."]);
}
}
break;
case 'POST':
if(!empty($input['name']) && !empty($input['price'])) {
$product->name = $input['name'];
$product->description = $input['description'] ?? '';
$product->price = $input['price'];
$product->category_id = $input['category_id'] ?? null;
if($product->create()) {
http_response_code(201);
echo json_encode(["message" => "Product created."]);
} else {
http_response_code(503);
echo json_encode(["message" => "Unable to create product."]);
}
} else {
http_response_code(400);
echo json_encode(["message" => "Unable to create product. Data is incomplete."]);
}
break;
case 'PUT':
if($id && !empty($input['name']) && !empty($input['price'])) {
$product->id = $id;
$product->name = $input['name'];
$product->description = $input['description'] ?? '';
$product->price = $input['price'];
$product->category_id = $input['category_id'] ?? null;
if($product->update()) {
http_response_code(200);
echo json_encode(["message" => "Product updated."]);
} else {
http_response_code(503);
echo json_encode(["message" => "Unable to update product."]);
}
} else {
http_response_code(400);
echo json_encode(["message" => "Unable to update product. Data is incomplete."]);
}
break;
case 'DELETE':
if($id) {
$product->id = $id;
if($product->delete()) {
http_response_code(200);
echo json_encode(["message" => "Product deleted."]);
} else {
http_response_code(503);
echo json_encode(["message" => "Unable to delete product."]);
}
} else {
http_response_code(400);
echo json_encode(["message" => "Unable to delete product. ID is missing."]);
}
break;
default:
http_response_code(405);
echo json_encode(["message" => "Method not allowed."]);
break;
}
?>
Part 2: AJAX Integration
2.1 Real-time Product Management with AJAX
<?php // ajax_product_manager.php session_start(); ?> <!DOCTYPE html> <html> <head> <title>AJAX Product Manager</title> <style> body { font-family: Arial, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; } .container { display: grid; grid-template-columns: 1fr 2fr; gap: 30px; } .section { background: #f8f9fa; padding: 25px; border-radius: 10px; } .form-group { margin-bottom: 15px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input, textarea, select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; } button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; } button:hover { background: #0056b3; } .product-item { background: white; padding: 15px; margin: 10px 0; border-radius: 5px; border-left: 4px solid #28a745; } .message { padding: 10px; margin: 10px 0; border-radius: 5px; } .success { background: #d4edda; color: #155724; } .error { background: #f8d7da; color: #721c24; } .loading { color: #6c757d; font-style: italic; } .actions { margin-top: 10px; } .actions button { margin-right: 5px; padding: 5px 10px; font-size: 0.9em; } .edit-btn { background: #ffc107; color: #212529; } .delete-btn { background: #dc3545; color: white; } </style> </head> <body> <h1>AJAX Product Manager</h1> <div class="container"> <!-- Product Form --> <div class="section"> <h2 id="form-title">Add New Product</h2> <form id="product-form"> <input type="hidden" id="product-id"> <div class="form-group"> <label>Product Name</label> <input type="text" id="name" required> </div> <div class="form-group"> <label>Description</label> <textarea id="description" rows="4"></textarea> </div> <div class="form-group"> <label>Price ($)</label> <input type="number" id="price" step="0.01" min="0" required> </div> <div class="form-group"> <label>Category</label> <select id="category_id"> <option value="1">Electronics</option> <option value="2">Books</option> <option value="3">Clothing</option> <option value="4">Home & Garden</option> </select> </div> <button type="submit" id="submit-btn">Add Product</button> <button type="button" id="cancel-btn" style="display: none; background: #6c757d;">Cancel</button> </form> <div id="form-message"></div> </div> <!-- Product List --> <div class="section"> <h2>Products</h2> <div id="product-list"> <div class="loading">Loading products...</div> </div> </div> </div> <script> const API_BASE = 'api/products/index.php'; let editingId = null; // Load products on page load document.addEventListener('DOMContentLoaded', loadProducts); // Form submission document.getElementById('product-form').addEventListener('submit', function(e) { e.preventDefault(); saveProduct(); }); // Cancel edit document.getElementById('cancel-btn').addEventListener('click', resetForm); async function loadProducts() { try { const response = await fetch(API_BASE); const data = await response.json(); const productList = document.getElementById('product-list'); if (data.data && data.data.length > 0) { productList.innerHTML = data.data.map(product => ` <div class="product-item" id="product-${product.id}"> <h3>${product.name}</h3> <p>${product.description}</p> <p><strong>Price: $${product.price}</strong></p> <p>Category: ${product.category_name}</p> <div class="actions"> <button class="edit-btn" onclick="editProduct(${product.id})">Edit</button> <button class="delete-btn" onclick="deleteProduct(${product.id})">Delete</button> </div> </div> `).join(''); } else { productList.innerHTML = '<p>No products found.</p>'; } } catch (error) { showMessage('Error loading products: ' + error.message, 'error'); } } async function saveProduct() { const formData = { name: document.getElementById('name').value, description: document.getElementById('description').value, price: document.getElementById('price').value, category_id: document.getElementById('category_id').value }; const submitBtn = document.getElementById('submit-btn'); submitBtn.disabled = true; submitBtn.textContent = 'Saving...'; try { let response; if (editingId) { // Update existing product response = await fetch(`${API_BASE}?id=${editingId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); } else { // Create new product response = await fetch(API_BASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); } const result = await response.json(); if (response.ok) { showMessage(result.message, 'success'); resetForm(); loadProducts(); } else { showMessage(result.message, 'error'); } } catch (error) { showMessage('Error saving product: ' + error.message, 'error'); } finally { submitBtn.disabled = false; submitBtn.textContent = editingId ? 'Update Product' : 'Add Product'; } } async function editProduct(id) { try { const response = await fetch(`${API_BASE}?id=${id}`); const product = await response.json(); if (response.ok) { document.getElementById('product-id').value = product.id; document.getElementById('name').value = product.name; document.getElementById('description').value = product.description; document.getElementById('price').value = product.price; document.getElementById('category_id').value = product.category_id; editingId = product.id; document.getElementById('form-title').textContent = 'Edit Product'; document.getElementById('submit-btn').textContent = 'Update Product'; document.getElementById('cancel-btn').style.display = 'inline-block'; // Scroll to form document.getElementById('product-form').scrollIntoView({ behavior: 'smooth' }); } } catch (error) { showMessage('Error loading product: ' + error.message, 'error'); } } async function deleteProduct(id) { if (!confirm('Are you sure you want to delete this product?')) { return; } try { const response = await fetch(`${API_BASE}?id=${id}`, { method: 'DELETE' }); const result = await response.json(); if (response.ok) { showMessage(result.message, 'success'); loadProducts(); } else { showMessage(result.message, 'error'); } } catch (error) { showMessage('Error deleting product: ' + error.message, 'error'); } } function resetForm() { document.getElementById('product-form').reset(); document.getElementById('product-id').value = ''; editingId = null; document.getElementById('form-title').textContent = 'Add New Product'; document.getElementById('submit-btn').textContent = 'Add Product'; document.getElementById('cancel-btn').style.display = 'none'; document.getElementById('form-message').innerHTML = ''; } function showMessage(message, type) { const messageDiv = document.getElementById('form-message'); messageDiv.innerHTML = `<div class="message ${type}">${message}</div>`; setTimeout(() => { messageDiv.innerHTML = ''; }, 5000); } </script> </body> </html>
Part 3: Email System with PHPMailer
3.1 Advanced Email System
<?php
// email/EmailService.php
require 'vendor/autoload.php'; // Install via: composer require phpmailer/phpmailer
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class EmailService {
private $mail;
public function __construct() {
$this->mail = new PHPMailer(true);
$this->setup();
}
private function setup() {
// Server settings
$this->mail->isSMTP();
$this->mail->Host = 'smtp.gmail.com';
$this->mail->SMTPAuth = true;
$this->mail->Username = 'your-email@gmail.com';
$this->mail->Password = 'your-app-password';
$this->mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$this->mail->Port = 587;
// Sender
$this->mail->setFrom('your-email@gmail.com', 'Your Store');
$this->mail->isHTML(true);
}
public function sendWelcomeEmail($toEmail, $toName) {
try {
$this->mail->addAddress($toEmail, $toName);
$this->mail->Subject = 'Welcome to Our Store!';
$body = $this->getWelcomeTemplate($toName);
$this->mail->Body = $body;
$this->mail->AltBody = strip_tags($body);
$this->mail->send();
return ['success' => true, 'message' => 'Welcome email sent successfully'];
} catch (Exception $e) {
return ['success' => false, 'message' => "Email could not be sent. Error: {$this->mail->ErrorInfo}"];
}
}
public function sendOrderConfirmation($toEmail, $toName, $orderDetails) {
try {
$this->mail->addAddress($toEmail, $toName);
$this->mail->Subject = 'Order Confirmation - #' . $orderDetails['order_id'];
$body = $this->getOrderTemplate($toName, $orderDetails);
$this->mail->Body = $body;
$this->mail->AltBody = strip_tags($body);
$this->mail->send();
return ['success' => true, 'message' => 'Order confirmation sent successfully'];
} catch (Exception $e) {
return ['success' => false, 'message' => "Email could not be sent. Error: {$this->mail->ErrorInfo}"];
}
}
private function getWelcomeTemplate($name) {
return "
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; padding: 20px; }
.container { max-width: 600px; background: white; padding: 30px; border-radius: 10px; margin: 0 auto; }
.header { background: #007bff; color: white; padding: 20px; text-align: center; border-radius: 5px; }
.content { padding: 20px 0; }
.button { background: #007bff; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px; display: inline-block; }
.footer { margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h1>Welcome to Our Store!</h1>
</div>
<div class='content'>
<h2>Hello, $name!</h2>
<p>Thank you for joining our community. We're excited to have you on board!</p>
<p>As a welcome gift, here's a 10% discount on your first purchase:</p>
<h3 style='text-align: center; color: #007bff;'>WELCOME10</h3>
<p>Start shopping now and discover our amazing products:</p>
<p style='text-align: center;'>
<a href='http://yourstore.com/shop' class='button'>Start Shopping</a>
</p>
</div>
<div class='footer'>
<p>If you have any questions, feel free to contact our support team.</p>
<p>Best regards,<br>The Store Team</p>
</div>
</div>
</body>
</html>
";
}
private function getOrderTemplate($name, $orderDetails) {
$itemsHtml = '';
foreach ($orderDetails['items'] as $item) {
$itemsHtml .= "
<tr>
<td>{$item['name']}</td>
<td>{$item['quantity']}</td>
<td>\${$item['price']}</td>
<td>\${$item['total']}</td>
</tr>
";
}
return "
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; background: #f4f4f4; padding: 20px; }
.container { max-width: 600px; background: white; padding: 30px; border-radius: 10px; margin: 0 auto; }
.header { background: #28a745; color: white; padding: 20px; text-align: center; border-radius: 5px; }
.content { padding: 20px 0; }
.order-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
.order-table th, .order-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
.order-table th { background: #f8f9fa; }
.total { font-size: 1.2em; font-weight: bold; color: #28a745; }
.footer { margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; }
</style>
</head>
<body>
<div class='container'>
<div class='header'>
<h1>Order Confirmed!</h1>
</div>
<div class='content'>
<h2>Hello, $name!</h2>
<p>Thank you for your order. We're processing it and will notify you when it ships.</p>
<h3>Order Details:</h3>
<table class='order-table'>
<tr>
<th>Product</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
</tr>
$itemsHtml
<tr>
<td colspan='3' style='text-align: right;'><strong>Subtotal:</strong></td>
<td>\${$orderDetails['subtotal']}</td>
</tr>
<tr>
<td colspan='3' style='text-align: right;'><strong>Shipping:</strong></td>
<td>\${$orderDetails['shipping']}</td>
</tr>
<tr>
<td colspan='3' style='text-align: right;'><strong>Total:</strong></td>
<td class='total'>\${$orderDetails['total']}</td>
</tr>
</table>
<h3>Shipping Address:</h3>
<p>{$orderDetails['shipping_address']}</p>
</div>
<div class='footer'>
<p>Order ID: #{$orderDetails['order_id']}</p>
<p>If you have any questions about your order, contact our support team.</p>
<p>Best regards,<br>The Store Team</p>
</div>
</div>
</body>
</html>
";
}
}
// Usage example
if ($_POST['action'] == 'send_welcome') {
$emailService = new EmailService();
$result = $emailService->sendWelcomeEmail($_POST['email'], $_POST['name']);
echo json_encode($result);
}
?>
Part 4: Complete E-commerce System
4.1 E-commerce Application with Cart & Checkout
<?php // ecommerce/index.php session_start(); class Cart { public function __construct() { if (!isset($_SESSION['cart'])) { $_SESSION['cart'] = []; } } public function addItem($productId, $quantity = 1, $productData = null) { if (isset($_SESSION['cart'][$productId])) { $_SESSION['cart'][$productId]['quantity'] += $quantity; } else { $_SESSION['cart'][$productId] = [ 'quantity' => $quantity, 'product' => $productData ]; } } public function updateQuantity($productId, $quantity) { if (isset($_SESSION['cart'][$productId])) { if ($quantity <= 0) { $this->removeItem($productId); } else { $_SESSION['cart'][$productId]['quantity'] = $quantity; } } } public function removeItem($productId) { unset($_SESSION['cart'][$productId]); } public function getCart() { return $_SESSION['cart']; } public function getTotal() { $total = 0; foreach ($_SESSION['cart'] as $item) { $total += $item['product']['price'] * $item['quantity']; } return $total; } public function getItemCount() { $count = 0; foreach ($_SESSION['cart'] as $item) { $count += $item['quantity']; } return $count; } public function clear() { $_SESSION['cart'] = []; } } class Order { private $db; public function __construct($db) { $this->db = $db; } public function create($userId, $cart, $shippingInfo) { try { $this->db->beginTransaction(); // Create order $query = "INSERT INTO orders (user_id, total, status, shipping_address) VALUES (:user_id, :total, 'pending', :shipping_address)"; $stmt = $this->db->prepare($query); $stmt->bindParam(':user_id', $userId); $stmt->bindParam(':total', $shippingInfo['total']); $stmt->bindParam(':shipping_address', $shippingInfo['address']); $stmt->execute(); $orderId = $this->db->lastInsertId(); // Create order items $query = "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (:order_id, :product_id, :quantity, :price)"; $stmt = $this->db->prepare($query); foreach ($cart as $productId => $item) { $stmt->bindParam(':order_id', $orderId); $stmt->bindParam(':product_id', $productId); $stmt->bindParam(':quantity', $item['quantity']); $stmt->bindParam(':price', $item['product']['price']); $stmt->execute(); } $this->db->commit(); return $orderId; } catch (Exception $e) { $this->db->rollBack(); return false; } } } // Initialize cart $cart = new Cart(); // Handle cart actions if (isset($_POST['action'])) { switch ($_POST['action']) { case 'add_to_cart': $cart->addItem($_POST['product_id'], $_POST['quantity'], [ 'name' => $_POST['product_name'], 'price' => $_POST['product_price'], 'image' => $_POST['product_image'] ]); break; case 'update_cart': $cart->updateQuantity($_POST['product_id'], $_POST['quantity']); break; case 'remove_from_cart': $cart->removeItem($_POST['product_id']); break; case 'clear_cart': $cart->clear(); break; } header('Location: ' . $_SERVER['PHP_SELF']); exit; } // Sample products $products = [ 1 => ['id' => 1, 'name' => 'Wireless Headphones', 'price' => 99.99, 'image' => 'headphones.jpg'], 2 => ['id' => 2, 'name' => 'Smart Watch', 'price' => 199.99, 'image' => 'watch.jpg'], 3 => ['id' => 3, 'name' => 'Laptop Backpack', 'price' => 49.99, 'image' => 'backpack.jpg'], 4 => ['id' => 4, 'name' => 'USB-C Cable', 'price' => 19.99, 'image' => 'cable.jpg'] ]; ?> <!DOCTYPE html> <html> <head> <title>PHP E-commerce Store</title> <style> body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f8f9fa; } .header { background: #343a40; color: white; padding: 15px 0; } .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; } .nav { display: flex; justify-content: space-between; align-items: center; } .cart-info { background: #007bff; padding: 10px 15px; border-radius: 20px; } .products { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 30px; margin: 40px 0; } .product-card { background: white; border-radius: 10px; padding: 20px; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } .product-image { width: 100%; height: 200px; object-fit: cover; border-radius: 5px; } .product-price { color: #28a745; font-size: 1.2em; font-weight: bold; margin: 10px 0; } .btn { background: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; } .btn-success { background: #28a745; } .cart-section { background: white; padding: 30px; border-radius: 10px; margin: 40px 0; } .cart-table { width: 100%; border-collapse: collapse; } .cart-table th, .cart-table td { padding: 15px; text-align: left; border-bottom: 1px solid #ddd; } .quantity-input { width: 60px; padding: 5px; text-align: center; } .checkout-btn { background: #28a745; color: white; padding: 15px 30px; border: none; border-radius: 5px; cursor: pointer; font-size: 1.1em; } </style> </head> <body> <div class="header"> <div class="container"> <div class="nav"> <h1>PHP Store</h1> <div class="cart-info"> Cart: <?= $cart->getItemCount() ?> items - $<?= number_format($cart->getTotal(), 2) ?> </div> </div> </div> </div> <div class="container"> <!-- Products Grid --> <h2>Featured Products</h2> <div class="products"> <?php foreach ($products as $product): ?> <div class="product-card"> <img src="images/<?= $product['image'] ?>" alt="<?= $product['name'] ?>" class="product-image"> <h3><?= $product['name'] ?></h3> <div class="product-price">$<?= number_format($product['price'], 2) ?></div> <form method="POST"> <input type="hidden" name="action" value="add_to_cart"> <input type="hidden" name="product_id" value="<?= $product['id'] ?>"> <input type="hidden" name="product_name" value="<?= $product['name'] ?>"> <input type="hidden" name="product_price" value="<?= $product['price'] ?>"> <input type="hidden" name="product_image" value="<?= $product['image'] ?>"> <input type="number" name="quantity" value="1" min="1" style="width: 60px; margin-right: 10px;"> <button type="submit" class="btn">Add to Cart</button> </form> </div> <?php endforeach; ?> </div> <!-- Shopping Cart --> <div class="cart-section"> <h2>Shopping Cart</h2> <?php if (empty($cart->getCart())): ?> <p>Your cart is empty.</p> <?php else: ?> <table class="cart-table"> <thead> <tr> <th>Product</th> <th>Price</th> <th>Quantity</th> <th>Total</th> <th>Actions</th> </tr> </thead> <tbody> <?php foreach ($cart->getCart() as $productId => $item): ?> <tr> <td><?= $item['product']['name'] ?></td> <td>$<?= number_format($item['product']['price'], 2) ?></td> <td> <form method="POST" style="display: inline;"> <input type="hidden" name="action" value="update_cart"> <input type="hidden" name="product_id" value="<?= $productId ?>"> <input type="number" name="quantity" value="<?= $item['quantity'] ?>" min="1" class="quantity-input"> <button type="submit" class="btn">Update</button> </form> </td> <td>$<?= number_format($item['product']['price'] * $item['quantity'], 2) ?></td> <td> <form method="POST" style="display: inline;"> <input type="hidden" name="action" value="remove_from_cart"> <input type="hidden" name="product_id" value="<?= $productId ?>"> <button type="submit" class="btn" style="background: #dc3545;">Remove</button> </form> </td> </tr> <?php endforeach; ?> </tbody> <tfoot> <tr> <td colspan="3" style="text-align: right;"><strong>Total:</strong></td> <td><strong>$<?= number_format($cart->getTotal(), 2) ?></strong></td> <td></td> </tr> </tfoot> </table> <div style="margin-top: 20px; text-align: center;"> <form method="POST" style="display: inline; margin-right: 10px;"> <input type="hidden" name="action" value="clear_cart"> <button type="submit" class="btn" style="background: #6c757d;">Clear Cart</button> </form> <button class="checkout-btn" onclick="window.location.href='checkout.php'">Proceed to Checkout</button> </div> <?php endif; ?> </div> </div> </body> </html>
Key Concepts Covered:
-
RESTful APIs: CRUD operations, proper HTTP status codes
-
AJAX Integration: Real-time updates without page reloads
-
Email System: Professional email templates with PHPMailer
-
E-commerce Features: Shopping cart, order management
-
Security: Input validation, CORS handling
-
Database Design: Proper relationships and transactions
Practice Exercises:
-
Add user authentication to the API
-
Implement product search and filtering
-
Add payment gateway integration (Stripe/PayPal)
-
Create an admin dashboard for order management
-
Implement product reviews and ratings
Next Lesson Preview:
In Lesson 5, we'll cover:
-
Advanced security practices
-
Performance optimization
-
Caching strategies
-
Deployment and DevOps
-
Building a complete SaaS application
You're now building professional-grade PHP applications with modern features! These skills are directly applicable to real-world web development projects.
What's Your Reaction?