Critical: Don’t use the same name for class and method. This triggers the class constructor:
// WRONG - triggers __construct behaviorclass Home extends Controller{ public function home() { } // Bad!}// CORRECTclass Home extends Controller{ public function index() { } // Good!}
The Songs controller demonstrates full CRUD operations:
application/controller/songs.php
class Songs extends Controller{ /** * PAGE: index * URL: /songs or /songs/index * Display all songs */ public function index() { // Fetch data from model $songs = $this->model->getAllSongs(); $amount_of_songs = $this->model->getAmountOfSongs(); // Variables are now available in views require APP . 'view/_templates/header.php'; require APP . 'view/songs/index.php'; require APP . 'view/_templates/footer.php'; } /** * ACTION: addSong * URL: /songs/addsong * Process form submission to add a song */ public function addSong() { if (isset($_POST["submit_add_song"])) { $this->model->addSong( $_POST["artist"], $_POST["track"], $_POST["link"] ); } // Redirect back to index header('location: ' . URL . 'songs/index'); } /** * ACTION: deleteSong * URL: /songs/deletesong/5 * Delete a song by ID */ public function deleteSong($song_id) { if (isset($song_id)) { $this->model->deleteSong($song_id); } header('location: ' . URL . 'songs/index'); } /** * PAGE: editSong * URL: /songs/editsong/5 * Display edit form for a song */ public function editSong($song_id) { if (isset($song_id)) { // Fetch song data $song = $this->model->getSong($song_id); // Display edit form with $song data require APP . 'view/_templates/header.php'; require APP . 'view/songs/edit.php'; require APP . 'view/_templates/footer.php'; } else { header('location: ' . URL . 'songs/index'); } } /** * ACTION: updateSong * URL: /songs/updatesong * Process form submission to update a song */ public function updateSong() { if (isset($_POST["submit_update_song"])) { $this->model->updateSong( $_POST["artist"], $_POST["track"], $_POST["link"], $_POST['song_id'] ); } header('location: ' . URL . 'songs/index'); } /** * AJAX ACTION: ajaxGetStats * URL: /songs/ajaxgetstats * Return data for AJAX request */ public function ajaxGetStats() { $amount_of_songs = $this->model->getAmountOfSongs(); // Simple API response echo $amount_of_songs; }}
public function addSong(){ // 1. Check if form was submitted if (isset($_POST["submit_add_song"])) { // 2. Process data (validation should go here) $this->model->addSong( $_POST["artist"], $_POST["track"], $_POST["link"] ); } // 3. Redirect to prevent form resubmission header('location: ' . URL . 'songs/index');}
POST-Redirect-GET pattern: After processing a POST request, always redirect to prevent duplicate submissions when users refresh the page.
public function customQuery(){ $sql = "SELECT * FROM song WHERE artist LIKE :search"; $query = $this->db->prepare($sql); $query->execute([':search' => '%' . $_GET['q'] . '%']); $results = $query->fetchAll(); // ... load views with $results ...}
// Bad: Logic in controllerpublic function addSong(){ if (strlen($_POST['artist']) < 3) { $error = "Artist name too short"; } if (!filter_var($_POST['link'], FILTER_VALIDATE_URL)) { $error = "Invalid URL"; } // More validation...}// Good: Logic in modelpublic function addSong(){ $result = $this->model->addSong( $_POST['artist'], $_POST['track'], $_POST['link'] ); if (!$result['success']) { // Handle error }}
2. Validate input
Always validate and sanitize user input:
public function deleteSong($song_id){ // Cast to integer $song_id = (int) $song_id; // Validate it's positive if ($song_id <= 0) { header('location: ' . URL . 'songs'); exit; } $this->model->deleteSong($song_id); header('location: ' . URL . 'songs');}
3. Use meaningful method names
// Goodpublic function editSong($song_id) { }public function updateSong() { }public function deleteSong($song_id) { }// Unclearpublic function action1() { }public function doStuff() { }
4. Don't mix display and action methods
// editSong() displays the formpublic function editSong($song_id){ $song = $this->model->getSong($song_id); require APP . 'view/songs/edit.php';}// updateSong() processes the formpublic function updateSong(){ $this->model->updateSong(/* ... */); header('location: ' . URL . 'songs');}