Bare-bone PHP Framework #3 Database & Routing

Bare-bone PHP Framework #3 Database & Routing

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']);

image.png

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.