How to build a custom PHP MVC Framework
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 🙂.