facebook youtube pinterest twitter reddit whatsapp instagram

Creating a Tiny PHP MVC Framework From Scratch

In this guide, we would go over creating a tiny PHP MVC Framework, this would sharpen your knowledge on how major frameworks (e.g Codeigniter or Laravel) works in general. I believe if you can understand the practical concept in this guide, you'll be able to relate the logic to the aforementioned framework real quick.

Basics of an MVC Pattern

I guess you already know what an MVC is, if you don't know, it is an acronym for Model-View-Controller. The easy way you can understand the way MVC pattern works is to first understand what happens when a request comes in from a server.

For example, if you enter devsrealm.com in your address bar:

1. URL In Address Bar

Before the devsrealm.com page renders, it would send a request to your web server, your server would process the request, and send back the output. In an MVC world, the process is still the same, just with a slight tweak beneath your application layer.

If a user requests a webpage, for example, devsrealm.com, your application would programmatically grab that this is a request for the homepage, and that is achievable by the Controller, so what is a controller?

A controller serves as the connector between the model and the view. It would be the one to examine the user URL or HTTP request to determine what should be done with it.

So, when the controller receives a request from a user or a client, it invokes the model (which is responsible for managing the data; storing, and retrieving, this is usually from a database) to perform the requested operations, and sends the data to the view.

The view is responsible for displaying the data provided by the model. Note that it is not the model that sends the data to the view, it is the controller that is controlling the two operations, it would make more sense when you see a practical example. It is something like this:

Controller: Hey, Mr. Model the user is requesting for the homepage of devsrealm.com, the user want the data of the homepage. Note here: I said data not display

Model: Sir Controller, here are the data.

Controller: Passes the data provided by the model to the view for display

View: The view then presents the information to the user, the View is the information that is being presented to a user, it would typically be a web-page

Hope you grabbed that, don't worry it would make more sense in a moment.

The Directory Structure

Create the following directory structure:

2. MVC Directory Structure

I would be using Nginx as my webserver, so, the first thing I'll be doing is redirects all user request to the index.php file in the public folder:

server {
        ## Your website name goes here.
        server_name drealmer.com;
        ## Your only path reference.
        root /media/sf_Devsrealm/Projects/drealmer;
        index index.php;

        # Since adding the public to our url is ugly, we would rewrite everything from public to the root url 

        location / {

               rewrite ^/$ /public/ break;
               rewrite ^(.*)$ /public$1 last;
        }
     

        # Redirect all public directory requests to index.php
        location /public {
               rewrite ^/public/(.*)$ /public/index.php?url=$1 last;
        }
        
        location ~ \.php$ {
            #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
            include fastcgi_params;
            fastcgi_intercept_errors on;
            #The following parameter can be also included in fastcgi_params file
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
            include snippets/fastcgi-php.conf;
        }
}

This line:

location / {

               rewrite ^/$ /public/ break;
               rewrite ^(.*)$ /public$1 last;
        }

would prevent having to add /public to your URL, normally, you would do something like this: devsrealm.com/public/index.php, adding the above nginx lines would prevent that, and thus you can use a pretty URL like: devsrealm.com

This line:

location /public {
               rewrite ^/public/(.*)$ /public/index.php?url=$1 last;
        }

would rewrite all requests to index.php, but why do this?

Doing this would not only make for bootstrapping, that is all calls go via our index.php but also means we have a single entry point. Then from there we can carry on the operation based on what the user is requesting.

Now, create files ending in php in the appropriate folder:

3. Files ending in .php

In index.php, add the following:

<?php
require_once '../app/boostrap.php';

// Init Core Library
new Core();

I should also point out that if you don't have a knowledge of OOP in PHP, then this guide is not for you. I have PHP and OOP Guide Here.

In boostrap.php, add:

<?php
// This would require all the necessary component

// Load Config
require_once 'config/config.php';
/*
 * Handled with autoloader
 */

// Autoload Core Libraries
spl_autoload_register(function ($className){
    require_once 'libraries/' .$className . '.php';
});

Normally, to require our classes, we would do something like this:

require_once 'libraries/Core.php';
require_once 'libraries/Controller.php';
require_once 'libraries/Database.php';

Imagine, having a hundred of those, good luck knowing what is what. PHP autoloader lets us load our Core libraries automatically, the file name needs to match the class name for this to work, if for example, your filename is Controller.php, then the class in that file should also be Controller, if your filename is Database.php, the class in Database.php needs to be Database. So, now, if we add more files in the libraries folder, it would automatically be loaded, thanks to the spl_autoload_register

When we add controllers, we'll be able to work with them regularly (without using includes/requires), that is the reason I added:

<?php
require_once '../app/boostrap.php';

// Init Core Library
new Core();

to the index.php, I guess you get the idea now.

The config: require_once 'config/config.php'; would contain our database details, file paths, websitename, etc. So, you might want to create a file name config.php in the config folder, in short, let's do that now

Config

Open up your config.php in the config folder, and add the necessary details like so:

<?php

// DATABASE DETAILS CONSTANT
define('DB_HOST', 'localhost');
define('DB_USER', 'YOUR_USERNAME');
define('DB_PASS', 'YOUR_PASS');
define('DB_NAME', 'YOUR_DATABASE_NAME');
define('DB_CHAR', 'utf8mb4');

// APP ROOT

define('APP_ROOT', dirname(dirname(__FILE__)));

// URL ROOT e.g http://example.com/
$root_url = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . '/';
define("URL_ROOT", $root_url);

// SITE NAME e.g example
$hostname = getenv('SERVER_NAME');
$cleanup = explode('.', $hostname);
define("SITE_NAME", $cleanup[0]);

Fill up the database with your details.

For the APP ROOT. The __FILE__ will be something like your_folder/app/config/config.php the first dirname would give us your_folder/app/config and the second dirname would then give us your_folder/app which is the App root.

Alternatively, you can do dirname(__DIR__);  We can then reuse the constant APP_ROOT whenever we need the app root in our files, this would be used to include files here and there.

We also get and define our website URL, and lastly, the SITE NAME get the site name, e.g if your URL is http://example.com/ your site name is example

getenv('SERVER_NAME') this returns the name of the host server e.g example.com, I then explode the value of the hostname variable into an array using the dot(.) as the delimiter. So, to get the sitename without the dot(.) I basically reference index 0 of the array.

Controller

You recall I said the controller is responsible for taking user/client requests, breaks it down, and then invokes the model and sends the response it gets from the model to the view for display.

In our index.php we added: new Core();so, all request received would go to the core controller, I guess you already have Core.php created in your libraries folder.

In Core.php, you add the following:

<?php
/*
 #
 # Core Class for Creating URL and Loading Core Controllers
 # URL Format - /controller/method/param
 */

class Core {
    protected $currentController = 'Pages';
    protected $currentmethod = 'index';
    protected $params = [];

    // Fetch the url paramters
    public function getUserURL() {
        if(isset($_GET['url'])) { // if the url param is set do below
            $url = rtrim($_GET['url'], '/');

            $url = filter_var($url, FILTER_SANITIZE_URL);

            $url = explode('/', $url);

            return $url; // return the url to whatever place that it is been called, in our case, it is called by the __contsruct above.
        }
    }

    public function __construct() {
        // If you want to output the value of the array use print_r($this->getUrl());
        $url = $this->getUserURL();

        /*
         * Since all our request are routed to index.php, we need to act as though this file is located in there
         * Which is why I have .. to move out from the public directory (where the index.php is located), and then we
         * move into /app/controllers
         */

        if(file_exists(APP_ROOT. '/controllers/'. ucwords($url[0]) . '.php')){
            // if the file exists, we set it as the current controller
            $this->currentController = ucwords($url[0]); // our default controller is Pages, which is defined above, anything founded would override it.
            /*
             * Unset the 0 index, The unset() function would delete index 0, but would leave the other indexes
             * You'll see the reason we ae using this below
             */
            // unset the 0 index
            unset($url[0]);
        }


        /*
         * Require the controller, if anything is not found, it would require the default pages, and instantiate it
         * if something is found, the founded file would have been set to the current controller above, we then require and instantiate it.
         */

        require_once APP_ROOT. '/controllers/'. $this->currentController . '.php';
        // instantiate controller class
        // if for example, the controller is Post, then it would be post = new Post
        $this->currentController = new $this->currentController;

        /*
         * Check for second parameter of the url, for example if we have pages/music/3,
         * then the second parameter would be music
         */

        if(isset($url[1])) {
            //check to see if methods exist in controller
            /*
             * method_exist takes two parameter, we are checking the currentcontroller first
             * and the method which is going to be the second part of the url
             */
            if(method_exists($this->currentController, $url[1])){
                // if the method is there, we set the current method
                // so, the method would have to exist in the $this->currentController class, for example Pages.
                $this->currentmethod = $url[1];
                // unset the 1 index
                unset($url[1]);
            }

          //  echo $this->currentmethod;
        }

        /*
         * Let's take care of the other parameters, by unsetting index 0 and 1, it is easy to take care of the rest
         * if there is paramters left in the $url we add them with the array_values, if otherwise, we default to empty array
         */
        // Get Params
        $this->params = $url ? array_values($url) : [];

        // call a callback with array of params
        call_user_func_array([$this->currentController, $this->currentmethod], $this->params);

    }
}

Let's understand it one at a time:

protected $currentController = 'Pages';
protected $currentmethod = 'index';
protected $params = [];

These are the Class properties that would hold the controller URL parameters, it would make sense in a second.

The method public function getUserURL() fetch the URL parameters.

For example, if the URL is devsrealm.com/?url=book, the function would return "book", and if you have devsrealm.com/post/edit/1, the function would return post/edit/1, the $_GET is doing the trick.  rtrim strips whitespace (or other characters) from the end of a string, in our case, we are stripping out the slash (/) for example, if the return parameter is post/edit/1/it would be post/edit/1 without the last slash (/)

filter_var is used to filter data. In our case, we want to use FILTER_SANITIZE_URL. The FILTER_SANITIZE_URL filter removes all illegal URL characters from a string. for example, if you have something like post/edit/��1, you probably don't want that kind of character in your URL, so it would sanitize the URL to post/edit/1

We then use explode to break up the string from the $url var into an array: $url = explode('/', $url);

The explode function would divide the string back into each of the values, this is useful when working with a comma-separated list or in our case, a slash, we tell the function to explode based on where the slash is located. For example, if I had post/edit/5, and I want to explode it by the slash delimiter into the var $url it would assign "post" to index 0, edit to index 1, and 5 to index 2, something like this: Array ( [0] => post [1] => edit [2] => 1 )

Learn more about Arrays and Array Functions 

We then return the $url.

Let's get to the constructor:

A constructor method is invoked when an object is created or instantiated You can use it to prepare the essential properties for an object.

This gets the returned URL by the getUerURL method, and stores it in the $url variable.

$url = $this->getUserURL();

We are basically using if condition to look in controllers for the first value in the array, for example, if post/edit/1 is in the array, we would be looking at post since it is the first likewise if you have page/add/1 in the array, we would be looking at page since it is the first value.

So, the file_exists function simply checks if the first value of the param in the $url variable e.g post/edit or page/edit has a file in the /app/controllers folder, the files are gonna look something like this /app/controller/post.php or /app/controller/page.php

We capitalize the first character in the value using ucwords, meaning post would be Post

if(file_exists(APP_ROOT. '/controllers/'. ucwords($url[0]) . '.php')){
    // if the file exists, we set it as the current controller
    $this->currentController = ucwords($url[0]); // our default controller is Pages, which is defined above, anything founded would override it.
    // unset the 0 index
    unset($url[0]);
}

This requires the controller, if anything is not found, it would require the default pages, and instantiate it. If something is found, the founded file would have been set to the current controller above, we then require and instantiate it.

require_once APP_ROOT. '/controllers/'. $this->currentController . '.php';
// instantiate controller class
// if for example, the controller is Post, then it would be post = new Post
 $this->currentController = new $this->currentController;

The next step is to check for the second parameter of the URL, for example, if we have pages/music/3, then the second parameter would be music. We are also using method_exist to check if a method exists at all. method_exist takes two parameters, we are checking the currentcontroller first and the method which is going to be the second part of the URL.

if(isset($url[1])) {
//check to see if methods exist in controller

    if(method_exists($this->currentController, $url[1])){
    // if the method is there, we set the current method
    // so, the method would have to exist in the $this->currentController class, for example Pages.
    $this->currentmethod = $url[1];
    // unset the 1 index
     unset($url[1]);
     }
}

Let's take care of the other parameters, since we have unset the index 0 and 1, it is easy to take care of the rest if there are parameters left in the $url we add them with the array_values, if otherwise, we default to an empty array

The call_user_func_array() function is a special way to call an existing PHP function. It takes a function to call as its first parameter, then takes an array of parameters as its second parameter.

Here is how the below would work, assuming $this->currentControlleris Pages, and the $this->currentmethodhas a method name dashboard then the below would do Pages->dashboard() the $this->paramswould be the argument that would be passed into the Pages->dashboard()

So, let's say you send a get request e.g example.com/pages/dashboard/33, then that would translate to Pages->dashboard(33).

We would create some form of method in the delegated file later: e.g Pages.php

// Get Param

$this->params = $url ? array_values($url) : [];

// call a callback with array of params

call_user_func_array([$this->currentController, $this->currentmethod], $this->params);

Now, let's move on to the base controller in our libraries folder, open up Controller.php, and add:

<?php
/*
 #
 # Base Controller
 # Loads the models and view
 #
 */

class Controller {
    // load model

    protected $model;
    /*
     * So, if post is passed to the model method, it would require the model
     * and then return it. Something like new Post();
     */
    public function model($model) {
        // Require model file
        require_once APP_ROOT. '/models/' .$model . '.php';

        // Instantiate model
        return new $model();
    }

    // Load view
    /*
     * View method takes two things, the view(where the view is located; the view file)
     * and optionally data array, should you need to pass data to the view.
     */
    public function view($view, $data = []){
        // check for view file
         (file_exists(APP_ROOT. '/views/' . $view . '.php')) ?
             require_once APP_ROOT. '/views/' .$view . '.php' : // Require view file
             die('View does not exist'); // View does not exit, Thus, stop the application
    }
}

This is just like our base controller, the actual controller in the controller folder would extend the base controller, the base controller basically contains the method to load the model and the view. The code is well documented, so, I guess you know what is going on.

Let's see an example, create Pages.php in your controller folder like so:

4. Pages-controller

Add the following code:

<?php

    class Pages extends Controller {
        public function __construct() {
        }

        public function index () { // default method if there is no method
            $data = array(
                'title' => 'Devsrealmer Framework',
            );
            $this->view('pages/index', $data);
        }

        public function about () {

            $data = array(
                'title' => 'Users Dashboard'
            );

           //  echo $id;
            $this->view('pages/dashboard', $data);


        }
    }

This is the Pages controller, so, it would be the one to handle requests to the model and view if it receives one. As you can see, I am extending the base controller I created using: class Pages extends Controller it the Pages class is the child class of the Controller class, and it inherits access to all the parent’s public and protected methods (with the exception of private methods). If we didn't do thing this way, then you would have something like this in your Pages controller:

<?php

    class Pages {
        // load model

        protected $model;
        public function model($model) {
            // Require model file
            require_once APP_ROOT. '/models/' .$model . '.php';

            // Instantiate model
            return new $model();
        }

        // Load view
        public function view($view, $data = []){
            // check for view file
            (file_exists(APP_ROOT. '/views/' . $view . '.php')) ?
                require_once APP_ROOT. '/views/' .$view . '.php' : // Require view file
                die('View does not exist'); // View does not exit, Thus, stop the application
        }

        public function __construct() {
        }

        public function index () { // default method if there is no method
            $data = array(
                'title' => 'Devsrealmer Framework',
            );
            $this->view('pages/index', $data);
        }

        public function about (/*$id = ""*/) {

            $data = array(
                'title' => 'About Us'
            );

           //  echo $id;
            $this->view('pages/about', $data);


        }

        public function dashboard () {

            $data = array(
                'title' => 'Users Dashboard'
            );

            //  echo $id;
            $this->view('pages/dashboard', $data);


        }
    }

Imagine having to do this in a couple of controllers, so, the best way is separating the load model and view into a controller, and we then extend it from within the children controllers, which is what we've done. So, here is the Pages controller again:

<?php

    class Pages extends Controller {
        public function __construct() {
        }

        public function index () { // default method if there is no method
            $data = array(
                'title' => 'Devsrealmer Framework',
            );
            $this->view('pages/index', $data);
        }

        public function about () {

            $data = array(
                'title' => 'Users Dashboard'
            );

           //  echo $id;
            $this->view('pages/dashboard', $data);


        }
    }

The index method is our default method, so, if there is no method, this is what you are going to see. We populated it with an array, and we then pass the result for the view to display:

 $data = array(
       'title' => 'Devsrealmer Framework',
 );
 $this->view('pages/index', $data);

We are not loading a model yet, just a simulation of how it would work, so, to load the view, create index.php under your views folder, and add the following:

<h1><?php echo $data['title']; ?></h1>

<p>Devsrealm PHP Framework for creating our application</p>

Since we passed in the associative array in the Pages.php doing: $this->view('pages/index', $data); we can access the title using the data array.

Now, if you go to your frontpage or website.com/pages/index you'll get:

Index.php view

To add a new view, e.g dashboard, we can do:

        public function dashboard () {

            $data = array(
                'title' => 'Users Dashboard'
            );

            //  echo $id;
            $this->view('pages/dashboard', $data);


        }

and as usual, you create a dashboard.php in your views folder, and we can access the data this way:

<h1><?php echo $data['title']; ?></h1>

and if you got to website.com/pages/dashboard, you will get:

6.The Dashboard view

You can add more views to the pages controller, and you can even create more controllers. e.g /post, etc

Model & View

The model is responsible for managing the data; storing, and retrieving, this is usually from a database to perform the requested operations, and sends the data to the view.

Now, go into your Database.php in the libraries folder, and add the following:

<?php

/*
 * PDO DATABASE CLASS
 */

class Database {
    /*
     * The properties are what we need to connect to the database
     */
    private $host = DB_HOST;
    private $user = DB_USER;
    private $pass = DB_PASS;
    private $dbname = DB_NAME;
    private $dbchar = DB_CHAR;

    // Database handler, we use it whenever we prepare SQL statements
    private $dbh;
    private $stmt; // for the actual statement
    private $err; // whenever we have an error, we store it here

    public function __construct() {
        // Set DSN
        $dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname . ';charset=' . $this->dbchar;
        // couple of options to use alongside dsn, not necessary but goo for enhancement
        // check doc for more options if you need one
        $options = array(
            PDO::ATTR_PERSISTENT => true, // This would persist db connection, and thus improve performance by checking if a db is already connected
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION // Handles error elegantly, e.g we can  catch errors easily
        );

        // Create PDO instance
        try {
            $this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
        } catch (PDOException $e) { // Catches PDOException if it is thrown
            $this->err = $e->getMessage();
            echo $this->err;
        }

    }

        public function query($sql) {
            $this->stmt = $this->dbh->prepare($sql);
        }

        /*
         * This is the bind the values
         * Binds a value to a corresponding named or question mark placeholder in the SQL statement that was used to prepare the statement.
         *
         * Learn more about switch-case statement here: https://devsrealm.com/web-dev/php/conditional-statements-in-php/#Switch-Case_Statements
         */
        public function bind($param, $value, $type = null){
            if(is_null($type)){
                switch(true){
                    case is_int($value): // if the value is an integer, set the $type to int
                        $type = PDO::PARAM_INT;
                        break;
                    case is_bool($value): // if the value is an integer, set the $type to bool
                        $type = PDO::PARAM_BOOL;
                        break;
                    case is_null($value): // if the value is an integer, set the $type to null
                        $type = PDO::PARAM_NULL;
                        break;
                    default: // if otherwise, set it to string
                        $type = PDO::PARAM_STR;
                }
            }
            /*
             * This function would actually bind the values, don't forget we are still within the public function bind() block,
             * so, we can access the $param, $value, $type below: $this->stmt->bindValue($param, $value, $type);
             *
             */

            $this->stmt->bindValue($param, $value, $type);
        }

        // We then Execute the prepared statement
        public function execute(){
            return $this->stmt->execute(); // return the statement to wherever it is been called
        }

    // Get result set as array of objects if more than one row
    public function resultSet(){
        $this->execute(); // calling the execute function. The one we created above
        return $this->stmt->fetchAll(PDO::FETCH_OBJ);
    }

    // Get single record as object for single row
    public function single(){
        $this->execute();
        return $this->stmt->fetch(PDO::FETCH_OBJ);
    }

    // Get row count
    public function rowCount(){
        return $this->stmt->rowCount();
    }
}

Learn more about PDO here: PDO: Querying Data From Database – PHP MariaDB/MySQL

This above is our PDO class, it can connect to users database, use a prepared statement, bind values, and return rows and results.

I won't cover everything as that is already covered in the PDO link. I'll start here:

// Create PDO instance
try {
     $this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
} catch (PDOException $e) { // Catches PDOException if it is thrown
     $this->err = $e->getMessage();
     echo $this->err;
}

Normally, we can connect to our db like so: $pdo = new PDO($dsn, $user, $pass);

By using the try-catch block we say try to connect a new PDO instance, that is try connecting to the database with the necessary param you have, dbh holds the PDO instance object, $this->dbh is just accessing the property of the dbh we created before.

If the connection fails, we catch the exceptions (PDOExceptions), the variable $e is a param for getting the error message (getMessage()) which is then stored to the $this->err property, and lastly, we echo the error: echo $this->err; and that would give us the error message if there is one.

Basically, When an exception is thrown, the catch clause in the invoking scope is called. The exception object is automatically passed in as the argument variable, in this case, it is passed to $e

Remember the model is concerned with the business logic and the application data. It can be used to perform data validations, process data, and store it, which is comes from or store in the db

So, The only place the DB connection and all is gonna work is the model, we would create one in a moment.

So, we've seen our to display views, we initially did something like so before:

$data = array(
      'title' => 'Devsrealmer Framework',
);
$this->view('pages/index', $data);

Which we then load in our view. Let's say I have the following data in my database:

MariaDB [test]> SELECT * FROM posts;
+---------+---------------------------------+
| post_id | title                           |
+---------+---------------------------------+
|       1 | What is Music                   |
|       2 | What is Love                    |
|       3 | Getting ready for our framework |
+---------+---------------------------------+

Before we create the post model, it is a good convention to have the model singular, and the controller plural. So, for the model both the file name and class would be Post, while in the controller both the file name and class would be Posts.

For each model to access the database, you create a property for the database:

class Post {
     private $db;

     public function __construct() {
         $this->db = new Database;
     }

     public function getPosts() {

         $this->db->query("SELECT * FROM posts");

         return $this->db->resultSet(); // return the result from the above query to wherever it is called from the controller.
     }
 }

The constructor is where we instantiate the DB class we created in the libraries/Database.php, so, we attached the instance of the DB class to the $db property.

The getPosts method would be used to retrieve the data from our database, we passed our SQL statement to the query method, which would prepare the statement for us.

The following line: return $this->db->resultSet(); returns the result from the above query to wherever it is called from the controller. The resultSet() is a method we created in libraries/Database.php, it deals with the execution of the query after it is has been prepared, and captures the result returned as a result of the execution.

To make this model work, you need to load it with the controller. Remember the controller deals with the user's requests for resources from the server. So, if the user request for example is example.com/pages/dashboard, the controller will load the post model to retrieve the post data then output the results in the list view.

In short, the controller links the models and views together depending on the requested resources.

Again, here is how my posts table looks in my client:

+---------+---------------------------------+
| post_id | title                           |
+---------+---------------------------------+
|       1 | What is Music                   |
|       2 | What is Love                    |
|       3 | Getting ready for our framework |
+---------+---------------------------------+

To load the view in the default page, I'll first load it with the controller, in my case, I am using Pages.php in my controller folder, I can load the model this way:

<?php

class Pages extends Controller {
    /**
     * @var mixed
     */
    private $postModel;

    public function __construct() {
        // The below is how we can load models with the controller
        $this->postModel = $this->model('Post');
    }

    public function index () { // default method if there is no method

        $posts = $this->postModel->getPosts();

        $data = array(
            'title' => 'Devsrealmer Framework',
            'artiste' => $posts
        );
        $this->view('pages/index', $data);
    }

    public function about (/*$id = ""*/) {

        $data = array(
            'title' => 'About Us'
        );

        //  echo $id;
        $this->view('pages/about', $data);


    }
}

First thing first, you shouldn't forget that this model is extending the base controller which loads the mode, and the view.

This is the portion of the base controller that loads the model:

public function model($model) {
    // Require model file
    require_once APP_ROOT. '/models/' .$model . '.php';

    // Instantiate model
    return new $model();
}

It would look in the model folder, for whatever we pass in the model argument, which in our case, it is Post, so, when you do this: $this->postModel = $this->model('Post'); in the model constructor, you are doing the following in the controller:

public function model($model = 'Post') {
    // Require model file
    require_once APP_ROOT. '/models/Post/.php';

    // Instantiate model
    return new $model();
}

You are basically loading the model. In short, if you print the value of the following:

$this->postModel = $this->model('Post');
print_r ($this->postModel);

You'll get the following details:

        Post Object
(
    [db:Post:private] => Database Object
        (
            [host:Database:private] => localhost
            [user:Database:private] => YOUR_USERNAME
            [pass:Database:private] => YOUR_PASSWORD
            [dbname:Database:private] => YOUR_DATABASE_NAME
            [dbchar:Database:private] => utf8mb4
            [dbh:Database:private] => PDO Object
                (
                )

            [stmt:Database:private] => 
            [err:Database:private] => 
        )

)

To then get the result of the SQL execution, we use the getpost() method we created in the Post model, which is why I added this: $posts = $this->postModel->getPosts(); in the index() method, we stored the result of that in the $post variable.

To then make it usable, we added a key-value pair to our array data, we then pass the result along to the view for display.

$data = array(
       'title' => 'Devsrealmer Framework',
       'artiste' => $posts
);
 $this->view('pages/index', $data);

So, in the view folder, I'll open the index.php, and add the following:

h1><?php echo $data['title']; ?></h1>

<ul>

    <?php
    foreach($data['artiste'] as $value){ ?>
        <li><?php echo $value->title; ?></li>
        <?php
    } ?>

</ul>

To output only the title we can use the foreach loop to loop through the array values and print only the value of the title. Here is how the data looks:

Array
(
    [title] => Devsrealmer Framework
    [artiste] => Array
        (
            [0] => stdClass Object
                (
                    [post_id] => 1
                    [title] => What is Music
                )

            [1] => stdClass Object
                (
                    [post_id] => 2
                    [title] => What is Love
                )

            [2] => stdClass Object
                (
                    [post_id] => 3
                    [title] => Getting ready for our framework
                )

        )

)

This is multidimensional data, since I am only interested in the title value of the artiste array, I used foreach loops that takes an array in $data, and says, I want you to only work on the artiste array, we add the special keyword “as” and the $value (it doesn’t have to be called $value) is going to be assigned an item as it loops through the items in the artiste array. <?php echo $value->title; ? would only print the value of the title in the array as it loops through.

Here is the final output:

7. Index.php model-view

If you access the URL homepage, e.g drealmer.com, you'll get the same output. Also, if the model doesn't this would be the default.

I'll conclude the guide here, this is not a full framework, I just wrote it to show you how the MVC architecture works behind the scene, this way fasten your approach when learning Laravel or Codeigniter.

Let me know if you have any questions.

Related Post(s)

  • Building Dependency Injection and The Container from Scratch in PHP

    In this guide, you'll learn about dependency injection and a way to build a simple DIC (Dependency Injection Container) in PHP using PSR-11 from scratch. First, What is an Ordinary Dependency? This

  • PHP Pluggable and Modular System – Part 2 (Implementation) [Event Dispatcher]

    In the first series of this guide, we discussed the theoretical aspect of building a pluggable system in PHP, I wrote a bit of code in that guide plus a couple of stuff you should avoid, you can lear

  • Best Way To Implement a Non-Breaking Friendly URL In PHP or Laravel or Any Language

    I was working on the link structure of my new Laravel app, and out of the blue I said: "What would happen if a user changes the slug of a post?" First Attempt - 301 Redirection The first solution I t

  • PHP Pluggable and Modular System - Part 1 (Abstract View) [Observable and Mediator Pattern]

    If there is one thing that is brutally confusing to me ever since I started coding, it would be a way to not only implement a Modular system but a way to make it extensible (adding and removing plugi

  • Object Oriented Programming Intro in PHP

    We recently went through the understanding of functions in PHP, well, that is one way of writing a PHP code, and the other way is using objects. In object-oriented programming, objects include data a

  • Static Methods and Properties In PHP

    We've recently dived deep into the basics of Object-Programming language in PHP, in this guide, we would look into static methods and properties. In the OOP guide, I said class describes the concept