How to build a custom PHP MVC Framework

Ojo Joseph Oluwaseun
6 min readJan 11, 2023
Model-View-Controller (MVC)

Have you ever considered playing around with a programming language in its core without using any framework or libraries? I had some free time during the holiday and I decided to spend a few hours playing around with just pure PHP, during this time I developed my own custom MVC framework and it is as easy as it can be.

Creating a custom Model-View-Controller (MVC) framework in PHP can be a challenging but rewarding task. MVC is a design pattern that separates an application into three main components: the model, the view, and the controller. This separation allows for better code organization and the ability to make changes to the application without affecting other parts of the code. In this post, we’ll walk through the steps of building a simple MVC framework in PHP that will help you understand the basics of MVC and how to build your own.

Step 1: Setting up the project

The first step is to create a new project directory and set up the basic file structure. We’ll need a few different directories to organize our code, such as one for controllers, one for views, and one for models. This will help keep our code organized and easy to understand. Below is an example of what the file structure might look like:

my_mvc_project/
controllers/
views/
models/
index.php

Step 2: Handling Requests

The next step is to handle incoming requests and route them to the appropriate controller. To do this, we’ll create an index.php file in the project root. This file will be the entry point for all incoming requests.

We’ll use the $_SERVER['REQUEST_URI'] variable to determine which controller should handle the request. For example, a request to /users/index should be handled by the Users controller's index action. We'll use regular expressions to extract the controller and action from the request URI. Below is an example of how we might extract the controller and action:

$url = $_SERVER['REQUEST_URI'];
$controller_name = 'Home';
$action_name = 'index';

if (preg_match('/\/([a-z]+)/i', $url, $matches)) {
$controller_name = ucfirst($matches[1]);
}

if (preg_match('/\/([a-z]+)/i', $url, $matches)) {
$action_name = $matches[1];
}

Step 3: Creating Controllers

Now that we can determine which controller should handle a request, we can create our controllers. Controllers are responsible for handling requests and updating the model. To keep our code organized, we’ll create a separate file for each controller in the controllers directory.

For example, if the request is handled by the Users controller, we'll include the controllers/Users.php file.

$controller_file = 'controllers/' . $controller_name . '.php';
if (file_exists($controller_file)) {
include($controller_file);
} else {
// handle 404 error
}

Step 4: Creating Views

The next step is to create the views for our application. Views are responsible for displaying the data to the user. We’ll create a separate file for each view in the views directory.

To keep our code organized, we’ll use the controller and action names to determine which view should be displayed. For example, a request to /users/index should be handled by the views/users/index.php view.

Step 5: Creating Models

Models are responsible for interacting with the database and representing the data for our application. They handle all the logic related to the data and provide an interface for the controllers to interact with the data. In this step, we will create a separate file for each model in the models directory.

To create a model, we can simply create a new PHP class and define any necessary properties and methods. For example, a User model might have properties such as id, username, and email, and methods such as get_by_id() and save().

It is important to note that models in MVC frameworks generally do not handle any logic that has to do with the presentation of data, that is the responsibility of the views. They focus mainly on data manipulation.

Once we have created the models, we can use them in our controllers to interact with the data. For example, in the Users controller's index action, we might use the User model to retrieve a list of users from the database and pass it to the view for display.

One of the core responsibilities of our model is to interact with the database and its connector.

To build a MySQL database connector in our custom MVC framework, we can create a new class that will handle all the interactions with the database. This class can be included in our models and used to interact with the database.

Here’s an example of what the Database class might look like:

class Database {
private $host = 'localhost';
private $username = 'root';
private $password = '';
private $db_name = 'my_mvc_project';
private $conn;

public function __construct() {
$this->conn = new mysqli($this->host, $this->username, $this->password, $this->db_name);
if ($this->conn->connect_error) {
die("Connection failed: " . $this->conn->connect_error);
}
}

public function query($sql) {
return $this->conn->query($sql);
}

public function close() {
$this->conn->close();
}
}

In this example, the Database class has a private variable $conn which is an instance of the mysqli class. This is the PHP class used to interact with MySQL databases. The class constructor will establish the connection to the database using the mysqli class. It takes 4 parameters: host, username, password, and database name. In this case, we have hardcoded them in the class but they can be passed in the constructor as well if you want to make the code more flexible.

The query method is used to execute a SQL query on the database. It takes one parameter, the SQL query, and returns the result of the query. And the close method is used to close the database connection.

Now, in our models, we can include this Database class and use it to interact with the database. For example, a User model might have a method such as get_by_id() which retrieves a user from the database by their ID.

class User {
private $db;

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

public function get_by_id($id) {
$sql = "SELECT * FROM users WHERE id = $id";
$result = $this->db->query($sql);
return $result->fetch_assoc();
}

public function save($user) {
// Prepare and execute SQL query to insert or update user data
}

public function __destruct() {
$this->db->close();
}
}

It is important to close the database connection when it is no longer needed to prevent resource leaks. In this example, the __destruct method is used to close the connection when the object is destroyed.

As you can see, this class provides a simple way to interact with the MySQL database and can be easily incorporated into the custom MVC framework. Keep in mind that this is just an example of one way to build a database connector in a custom MVC framework, it is important that you consider security when interacting with the database, such as using prepared statements to avoid SQL injection.

Step 6: Using Composer

Using Composer is a great way to manage dependencies in a PHP project, and it’s especially useful when building a custom MVC framework. Composer is a dependency manager for PHP that allows you to easily include and manage third-party libraries and packages in your project.

Here are the steps to use Composer in our custom MVC framework:

Step A: Install Composer

To use Composer, you’ll first need to have it installed on your machine. You can download the installer from the Composer website and run it to install Composer.

Step B: Create a composer.json file

The next step is to create a composer.json file in the root of your project. This file will define the dependencies for your project.

For example, if you want to use a database library such as Doctrine ORM to interact with the database, you would add it to the require section of the composer.json file:

{
"require": {
"doctrine/orm": "^2.7"
}
}

Step C: Install Dependencies

Once the composer.json file is created, you can run the composer install command to install the dependencies.

When you run this command, Composer will automatically download the required libraries and packages and place them in a vendor directory.

Step D: Autoload Dependencies

The final step is to autoload the dependencies so that they can be easily used throughout your project. Composer provides an autoloader, which you can include in your project.

require 'vendor/autoload.php';

Now you can use the Doctrine\ORM\EntityManager class to interact with your database.

By using Composer, you can easily manage dependencies in your project, including updating and removing them as needed. It also allows you to easily share your project with others, as they can simply run the composer install command to install all the dependencies.

It is important to note that there are other ways to use composer in a PHP project and this is just an example of one way to use it. Also, this is just an example of how to use a library in composer, it will depend on what you want to achieve, so you may have to research and decide what is the best library for your specific needs.

I hope this post helps and have a lovely day 🙂.

--

--

Ojo Joseph Oluwaseun

I’m Joseph, I have over 8 years of experience building Web Applications, Middleware technology, user interface design and Consulting.