Database
Currently, our model is a bit useless, so let's make it use data from Database instead of a hardcoded one.
First, create a database, create table books
, and add at least one entry.
CREATE TABLE `books` (
`id` int NOT NULL AUTO_INCREMENT UNIQUE,
`title` varchar(256) DEFAULT NULL,
`author` varchar(256) DEFAULT NULL,
`price` decimal(11,2) DEFAULT NULL,
PRIMARY KEY (`id`)
);
Now in the Core folder, add Database.php
Define properties for connection to Database: host, port, username, password, and database name. And connect
method to connect to our Database using PDO
<?php
namespace Core;
class Database
{
private static $host = '127.0.0.1';
private static $port = '3310';
private static $username = 'root';
private static $password = 'toor';
private static $dbname = 'products';
public function connect()
{
try {
// Connection string for mysql
$string = "mysql:host=" . self::$host . ";port=" . self::$port . ";dbname=" . self::$dbname;
// New PDO instance/connection
$pdo = new \PDO($string, self::$username, self::$password);
// Add attribute to PDO to make it throw errors
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (\PDOException $e) {
echo "Connection failed: " . $e->getMessage();
}
}
}
Now alter Book.php
to use Database to fetch data.
<?php
namespace Models;
use Core\Database;
class Book {
public static $table = 'books';
private $database;
// Adding dependency on Database that need to be injected
public function __construct(Database $database)
{
$this->database = $database;
}
public function get() {
// Sql query
$sql = "SELECT * FROM " . self::$table;
// connect to database and define query to execute
$stmt = $this->database->connect()->query($sql);
// execute query
$stmt->execute();
// fetch all results and use column names as keys
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}
Now in MainController.php
<?php
namespace Controllers;
use Models\Book;
class MainController
{
public function Home()
{
// Using container to get an instance of Book, instead on writing new Book(new Database())
$books = container()->make(Book::class)->get();
require_once './Views/home.phtml';
}
}
Routing
Now our system can respond to only one route /
and execute only one method, that's peaty useless. So to be able to define as many routes as we like, we need Router.
Create file Route.php
in Core
<?php
namespace Core;
class Route
{
public static function get($route, $callback)
{
// Resolve if request in GET
if ($_SERVER['REQUEST_METHOD'] !== 'GET') return;
self::resolve($route, $callback);
}
public static function post($route, $callback)
{
// Resolve if request in POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;
self::resolve($route, $callback);
}
private static function resolve($route, $callback)
{
// Check if the route is the same as the current URL
if ($route == $_SERVER['REQUEST_URI']) {
if (gettype($callback) === 'array') {
// Using container resolve dependencies and invoke callback
container()->call($callback[0], $callback[1]);
} else {
// Invoke the callback function
$callback();
}
}
}
}
and In Index.php
use router instead of calling controller directly
use Core\{Route, Container};
use Controllers\MainController;
Router::get('/', [MainController::class, 'Home']);
Congratulations, now we have a Router that invokes the necessary controller, a Controller that executes all logic for a particular request/route, a Model that can interact with the Database, a Database class to connect with the Database, and a View to show our data.