Models handle all database interactions and business logic in MINI. They encapsulate SQL queries, data validation, and data transformations, keeping this logic separate from controllers and views.MINI uses PDO (PHP Data Objects) for secure, database-agnostic data access with prepared statements.
/** * Get all songs from database */public function getAllSongs(){ $sql = "SELECT id, artist, track, link FROM song"; $query = $this->db->prepare($sql); $query->execute(); // fetchAll() returns all result rows return $query->fetchAll();}
Use in controller:
$songs = $this->model->getAllSongs();// Array of objects: $songs[0]->artist, $songs[0]->track, etc.
/** * Get a single song from database */public function getSong($song_id){ $sql = "SELECT id, artist, track, link FROM song WHERE id = :song_id LIMIT 1"; $query = $this->db->prepare($sql); $parameters = array(':song_id' => $song_id); $query->execute($parameters); // fetch() returns exactly one result return $query->fetch();}
:song_id is a named placeholder. PDO will safely escape the value, preventing SQL injection.
/** * Update a song in database * @param string $artist Artist name * @param string $track Track name * @param string $link Link URL * @param int $song_id Song ID */public function updateSong($artist, $track, $link, $song_id){ $sql = "UPDATE song SET artist = :artist, track = :track, link = :link WHERE id = :song_id"; $query = $this->db->prepare($sql); $parameters = array( ':artist' => $artist, ':track' => $track, ':link' => $link, ':song_id' => $song_id ); $query->execute($parameters);}
/** * Delete a song from the database * @param int $song_id ID of song to delete */public function deleteSong($song_id){ $sql = "DELETE FROM song WHERE id = :song_id"; $query = $this->db->prepare($sql); $parameters = array(':song_id' => $song_id); $query->execute($parameters);}
/** * Get count of songs */public function getAmountOfSongs(){ $sql = "SELECT COUNT(id) AS amount_of_songs FROM song"; $query = $this->db->prepare($sql); $query->execute(); // Access the aliased column return $query->fetch()->amount_of_songs;}
// 1. Prepare the statement with placeholders$sql = "SELECT * FROM song WHERE artist = :artist AND year > :year";$query = $this->db->prepare($sql);// 2. Bind parameters (PDO escapes them automatically)$parameters = array( ':artist' => $user_input_artist, // Safe even with special characters ':year' => $user_input_year);// 3. Execute with parameters$query->execute($parameters);// 4. Fetch results$results = $query->fetchAll();
Please note that it’s not necessary to “clean” our input in any way. With PDO all input is escaped properly automatically. We also don’t use strip_tags() etc. here so we keep the input 100% original (so it’s possible to save HTML and JS to the database, which is a valid use case). Data will only be cleaned when putting it out in the views.
public function addSong($artist, $track, $link){ // No need for htmlspecialchars() or strip_tags() // PDO handles SQL escaping $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; $query = $this->db->prepare($sql); $query->execute([ ':artist' => $artist, // Raw input, safely escaped by PDO ':track' => $track, ':link' => $link ]);}
public function searchSongs($artist, $min_year){ $sql = "SELECT * FROM song WHERE artist LIKE :artist AND year >= :min_year ORDER BY year DESC"; $query = $this->db->prepare($sql); $query->execute([ ':artist' => '%' . $artist . '%', // LIKE with wildcards ':min_year' => $min_year ]); return $query->fetchAll();}
public function getSongsWithGenre(){ $sql = "SELECT song.*, genre.name AS genre_name FROM song INNER JOIN genre ON song.genre_id = genre.id ORDER BY song.artist"; $query = $this->db->prepare($sql); $query->execute(); return $query->fetchAll();}
// Fetch all rows$query->fetchAll(); // Array of objects// Fetch one row$query->fetch(); // Single object// Fetch single column value$query->fetchColumn(); // Scalar value// Fetch as associative array$query->fetchAll(PDO::FETCH_ASSOC);// Fetch with custom class$query->fetchAll(PDO::FETCH_CLASS, 'Song');
// Bad$sql = "SELECT * FROM song WHERE id = $id";// Good$sql = "SELECT * FROM song WHERE id = :id";$query->execute([':id' => $id]);
2. Return data, don't echo
// Badpublic function getAllSongs(){ $query = $this->db->query("SELECT * FROM song"); foreach ($query->fetchAll() as $song) { echo $song->artist; // Don't output in model! }}// Goodpublic function getAllSongs(){ $query = $this->db->prepare("SELECT * FROM song"); $query->execute(); return $query->fetchAll(); // Return data}
3. Validate in models
public function addSong($artist, $track, $link){ // Validate before inserting if (strlen($artist) < 2) { return ['success' => false, 'error' => 'Artist name too short']; } if (!filter_var($link, FILTER_VALIDATE_URL)) { return ['success' => false, 'error' => 'Invalid URL']; } // Insert $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)"; $query = $this->db->prepare($sql); $query->execute([':artist' => $artist, ':track' => $track, ':link' => $link]); return ['success' => true, 'id' => $this->db->lastInsertId()];}
4. Handle errors gracefully
public function getSong($song_id){ $sql = "SELECT * FROM song WHERE id = :id LIMIT 1"; $query = $this->db->prepare($sql); $query->execute([':id' => $song_id]); $result = $query->fetch(); // Check if song exists if (!$result) { return null; // Or throw exception } return $result;}