Lab 5: Implementing READ and CREATE Operations
Additional Resources
Section titled “Additional Resources”- Slim 4 Routing ↗
- Working with the BaseModel class
- Working with the BaseController class
- Database Tables
Overview
Section titled “Overview”In this lab, you will implement two core CRUD operations in your admin panel: READ (listing products) and CREATE (adding new products).
- In Part I, you will build a product listing page that displays all products from the database in a table.
- In Part II, you will implement the CREATE operation, which involves displaying a creation form (GET request) and handling the form submission (POST request).
- You will apply the Post-Redirect-Get (PRG) pattern to prevent duplicate submissions and use the FlashMessage helper (from Lab 4) to provide user feedback.
Learning Objectives
Section titled “Learning Objectives”By completing this lab, you will:
- Build a product listing page that queries and displays data from the database.
- Implement the CREATE operation with GET (display form) and POST (handle submission) routes.
- Build controller methods that handle form data extraction and validation.
- Create model methods to fetch records and insert new ones.
- Build an HTML form that submits data to the correct POST route.
- Apply the Post-Redirect-Get (PRG) pattern to prevent duplicate submissions.
- Integrate FlashMessage for success and error feedback after form submission.
Prerequisites
Section titled “Prerequisites”Before starting this lab, ensure you have:
- Completed Lab 2 (admin panel with route group, admin layout, and ViewHelper).
- Completed Lab 4 (FlashMessage helper class).
- The
productstable in your database with sample data.
Part I: READ Operation (Product Listing)
Section titled “Part I: READ Operation (Product Listing)”In this part, you will create the ProductsModel and ProductsController classes, then build a page that lists all products from the database in the admin panel.
Step 1: Enable Flash Messages in the Admin Layout
Section titled “Step 1: Enable Flash Messages in the Admin Layout”Objective: Uncomment the FlashMessage rendering in the admin header so flash messages display on all admin pages.
Instructions:
- Open
app/Views/admin/adminHeader.php - Find the commented-out flash message block:
<!-- Flash Messages (uncomment when FlashMessage helper is available) --><!-- <div class="mb-3"> <?= App\Helpers\FlashMessage::render() ?></div> -->- Uncomment the block so it looks like this:
<!-- Flash Messages --><div class="mb-3"> <?= App\Helpers\FlashMessage::render() ?></div>- Save the file
Step 2: Create the ProductsModel
Section titled “Step 2: Create the ProductsModel”Objective: Create a model class for querying the products table.
Instructions:
- Navigate to your
app/Domain/Models/directory - Create a new file named
ProductsModel.php - Copy and paste the following class skeleton:
<?php
namespace App\Domain\Models;
use App\Helpers\Core\PDOService;
class ProductsModel extends BaseModel{ public function __construct(PDOService $pdoService) { parent::__construct($pdoService); }
/** * Fetch all products from the database. */ public function getAll(): array { // TODO: Execute a SELECT query to fetch all products // - Select: id, name, price, stock_quantity, created_at // - Order by: created_at DESC (newest first) // - Use $this->fetchAll() to return all rows as an array }
/** * Fetch all categories for the product form dropdown. */ public function getAllCategories(): array { // TODO: Execute a SELECT query to fetch all categories (id, name) // - Order by: name ASC // - Use $this->fetchAll() to return all rows as an array }}- Implement the TODO in the
getAll()method - Save the file
Step 3: Create the ProductsController
Section titled “Step 3: Create the ProductsController”Objective: Create a controller class that uses the ProductsModel to handle product-related requests.
Instructions:
- Navigate to your
app/Controllers/directory - Create a new file named
ProductsController.php - Copy and paste the following class skeleton:
<?php
namespace App\Controllers;
use App\Domain\Models\ProductsModel;use DI\Container;use Psr\Http\Message\ResponseInterface as Response;use Psr\Http\Message\ServerRequestInterface as Request;
class ProductsController extends BaseController{ public function __construct( Container $container, private ProductsModel $productsModel ) { parent::__construct($container); }
/** * Display the list of all products. */ public function index(Request $request, Response $response, array $args): Response { // TODO: 1. Fetch all products using $this->productsModel->getAll()
// TODO: 2. Create a $data array with 'title' and 'products' keys
// TODO: 3. Render the view 'admin/products/products.IndexView.php' and pass $data }}- Implement the TODO comments in the
index()method - Save the file
Step 4: Register the Product List Route
Section titled “Step 4: Register the Product List Route”Objective: Add a route to display the product listing page.
Instructions:
- Open
app/Routes/web-routes.php - Add the following import at the top:
use App\Controllers\ProductsController;- Inside the existing
$app->group('/admin', ...)block, add a GET route for the product list:
$app->group('/admin', function ($group) { // ... existing dashboard route ...
// TODO: Add GET route for /products // - Controller: ProductsController::class, 'index' // - Named route: 'products.index'});- Save the file
Step 5: Create the Product Listing View
Section titled “Step 5: Create the Product Listing View”Objective: Build a view that displays all products in an HTML table.
Instructions:
- Navigate to
app/Views/admin/products/(create theproducts/folder if it does not exist) - Create a new file named
products.IndexView.php - Set up the view structure:
<?php
use App\Helpers\ViewHelper;
ViewHelper::loadAdminHeader($title);?>
<!-- TODO: Add an "Add Product" button/link that points to the create form -->
<div class="table-responsive"> <table class="table table-striped table-hover"> <thead> <tr> <th>ID</th> <th>Name</th> <th>Price</th> <th>Stock</th> <th>Created At</th> </tr> </thead> <tbody> <?php foreach ($products as $product): ?> <tr> <!-- TODO: Display each product's id, name, price, stock_quantity, and created_at --> <!-- Hint: Use hs() for text fields and number_format() for the price --> </tr> <?php endforeach; ?> </tbody> </table></div>
<?php ViewHelper::loadAdminFooter(); ?>- Save the file
Step 6: Test the Product Listing
Section titled “Step 6: Test the Product Listing”Objective: Verify that the product listing page displays correctly.
Instructions:
- Start your development server (if not already running)
- Visit
http://localhost/[your-app-name]/admin/productsin your browser - Verify that:
- The page displays all products from the database in a table
- Product names, prices, stock quantities, and creation dates are shown correctly
- The admin layout (sidebar, header) wraps the content
Part II: CREATE Operation
Section titled “Part II: CREATE Operation”In this part, you will add the ability to create new products from the admin panel.
Step 7: Register CREATE Routes
Section titled “Step 7: Register CREATE Routes”Objective: Add two routes for the CREATE operation inside the existing /admin route group.
Instructions:
- Open
app/Routes/web-routes.php - Inside the
/admingroup (below the product list route from Step 4), add two routes:
$app->group('/admin', function ($group) { // ... existing dashboard and products index routes ...
// TODO: Add GET route for /products/create // - Controller: ProductsController::class, 'create' // - Named route: 'products.create'
// TODO: Add POST route for /products // - Controller: ProductsController::class, 'store'});- Save the file
Step 8: Implement Controller Methods
Section titled “Step 8: Implement Controller Methods”Objective: Add create() and store() methods to ProductsController with FlashMessage integration.
Instructions:
- Open
app/Controllers/ProductsController.php - Add the following import at the top:
use App\Helpers\FlashMessage;Method 1: create() - Display the Creation Form
Section titled “Method 1: create() - Display the Creation Form”- Add the
create()method that renders the product creation form:
public function create(Request $request, Response $response, array $args): Response{ // TODO: 1. Fetch all categories using $this->productsModel->getAllCategories()
// TODO: 2. Create a $data array with 'title', and 'categories' keys
// TODO: 3. Render the view 'admin/products/products.CreateView.php' and pass $data}Method 2: store() - Handle Form Submission
Section titled “Method 2: store() - Handle Form Submission”- Add the
store()method that processes the submitted form data:
public function store(Request $request, Response $response, array $args): Response{ // TODO: 1. Get form data using $request->getParsedBody()
// TODO: 2. Validate required fields (name and price) // - If validation fails, set an error flash message and redirect back to 'products.create'
// TODO: 3. Save to database using $this->productsModel->createAndGetId()
// TODO: 4. Set a success flash message
// TODO: 5. Redirect to the product list ('products.index')}- Save the file
Step 9: Implement the Model Method
Section titled “Step 9: Implement the Model Method”Objective: Add a method to ProductsModel that inserts a new product and returns its ID.
Instructions:
- Open
app/Domain/Models/ProductsModel.php - Add the following method:
public function createAndGetId(array $data): string{ // TODO: 1. Execute an INSERT query using $this->execute() // - Insert: category_id, name, price, description, created_at // - Use named parameters (:category_id, :name, :price, :description, :created_at)
// TODO: 2. Return the last inserted ID using $this->lastInsertId()}- Save the file
Step 10: Create the Product Creation Form View
Section titled “Step 10: Create the Product Creation Form View”Objective: Build the HTML form that submits product data to the POST route.
Instructions:
- Navigate to
app/Views/admin/products/ - Create a new file named
products.CreateView.php - Set up the view structure:
<?php
use App\Helpers\ViewHelper;
ViewHelper::loadAdminHeader($title);?>
<form method="POST" action="<?= APP_BASE_URL ?>/admin/products"> <!-- TODO: Add a select dropdown for category (required) --> <!-- Use ViewHelper::renderSelectOptions($categories, '', 'id', 'name') --> <!-- to generate the <option> elements -->
<!-- TODO: Add input field for product name (text, required) -->
<!-- TODO: Add input field for price (number, step="0.01", required) -->
<!-- TODO: Add textarea for description (optional) -->
<!-- TODO: Add submit button -->
<!-- TODO: Add cancel link back to the admin product list --></form>
<?php ViewHelper::loadAdminFooter(); ?>- Save the file
Step 11: Test the CREATE Operation
Section titled “Step 11: Test the CREATE Operation”Objective: Verify that the CREATE operation works correctly with flash messages.
Instructions:
- Visit
http://localhost/[your-app-name]/admin/products/createin your browser - Test the following scenarios:
- Submit the form with empty fields: you should see an error flash message and be redirected back to the form
- Fill in valid product data and submit: you should see a success flash message and be redirected to the product list
- Check your database to confirm the new product was inserted correctly
- Verify the new product appears in the product listing page from Part I
- Refresh the page after a successful creation: no duplicate product should be inserted (PRG pattern working)