CRUD with PHP OOP and MVC Design Pattern

What is MVC?

From Wikipedia

Model–View–Controller (MVC) is an architectural pattern used in software engineering. Successful use of the pattern isolates business logic from user interface considerations, resulting in an application where it is easier to modify either the visual appearance of the application or the underlying business rules without affecting the other. In MVC, the model represents the information (the data) of the application; the view corresponds to elements of the user interface such as text, checkbox items, and so forth; and the controller manages the communication of data and the business rules used to manipulate the data to and from the model.

  • Model – represents applications behavior and data. It is also commonly known as the domain. The domain represents the problem you are trying to solve.
  • View – represents the presentation. It can query the model about its state and generate the presentation of the model objects.
  • Controller – represents applications workflow, it processes the request parameters and decides what to do based on them. This, usually, involves manipulating the model and displaying the data in the selected view.

MVC Design Pattern

As I think about the MVC pattern I can see only one negative aspect and several positive aspects. The negative is that the MVC pattern introduces complexity into the project which can be a burden for simple applications, but as your application grows this negative aspect is over weighted by positive aspects. I am going to describe few positive aspects I can think of.

MVC separates the model from the view, that is, the data from its representation. The separation of a model from its representation is one of the basic rules of a good software design. When you separate a model from its representation you can easily add many different representations of the same model. For example, a web application typically has HTML representation, used by web browsers, and JSON representation used by JavaScript AJAX clients.

A distinctive MVC role allows us to better distribute manpower on a big project. For example, domain and database experts can work on a model while web designers work on a view. Because each part is developed by specialized developers, an application can be of better quality. This also affects development time, projects can be build quicker because specialized developers can work simultaneously, each in their area of expertise, without affecting other developers work (to much).

Maintenance and upgrades of a complex application are easier because a developer can look at the application as a series of “modules” each consisting of Model, View and the Controller part. A developer can modify one “module” and does not need to worry that the changes he introduced will affect other parts of the system (I believe that this is called separation of concerns). Also, when adding new functionality to the application, a developer can simply create a new “module”.

Example application

For this tutorial I created simple contacts manager which supports basic CRUD operations. User can add a new contact, delete it, display a contact detail and display list of all contacts. I will refer to these operations as actions. Therefore, this application has four different actions: add contact, delete contact, show contact and list contacts. You can see the result of the list contacts action in the screenshot below:

MVC CRUD

The implementation

The implementation consists of the index.php file and several files placed in the model, view and controller directories:

PHP OOP MVC CRUD Folder Structure

The index.php script is central access point, all requests go through it.

The controller is defined in the controller directory:

  1.  ContactsController.php file.

Application views are defined in the view directory:

  1. contact-form.php is responsible for displaying “Add new contact” form to the user
  2. contact.php is responsible for displaying contact details, contacts.php is responsible for displaying the contacts list
  3.  error.php is responsible for diplaying errors.

The model is defined in the model directory:

It consists of three parts:

  1. ContactsGateway.php is a table data gateway to the database table I’ll show you later, the
  2. ContactsService.php object defines the model API that is used by the controller.
  3. ValidationException.php is an exception thrown from the model and catched by the controller in case of any validation errors. Using the ValidationException the model can alert the controller about validation errors and the controller can pass them to the view so they can be displayed.

The model

Before I explain source code, you must know something about the model. The model has a single entity – Contact which is persisted in the contacts table. The Contact has no behavior so I used SQL table structure to define it:


CREATE TABLE `contacts` (

    `id` int(11) NOT NULL AUTO_INCREMENT,

    `name` varchar(128) NOT NULL,

    `phone` varchar(64) DEFAULT NULL,

    `email` varchar(255) DEFAULT NULL,

    `address` varchar(255) DEFAULT NULL,

    PRIMARY KEY (`id`)

)

There is no class in the model that represents the Contact entity, instead I used standard PHP objects automatically created from the database record (I know, it is not very OO but it was quick).

What can we do with that incredible model? Here is the public interface of the ContactsService class where you can see all the features of the model:


class ContactsService {

private $contactsGateway    = NULL;

private function openDb() {
if (!mysql_connect("localhost", "root", "")) {
throw new Exception("Connection to the database server failed!");
}
if (!mysql_select_db("mvc-crud")) {
throw new Exception("No mvc-crud database found on database server.");
}
}

private function closeDb() {
mysql_close();
}

public function __construct() {
$this->contactsGateway = new ContactsGateway();
}

public function getAllContacts($order) {
try {
$this->openDb();
$res = $this->contactsGateway->selectAll($order);
$this->closeDb();
return $res;
} catch (Exception $e) {
$this->closeDb();
throw $e;
}
}

public function getContact($id) {
try {
$this->openDb();
$res = $this->contactsGateway->selectById($id);
$this->closeDb();
return $res;
} catch (Exception $e) {
$this->closeDb();
throw $e;
}
return $this->contactsGateway->find($id);
}

private function validateContactParams( $name, $phone, $email, $address ) {
$errors = array();
if ( !isset($name) || empty($name) ) {
$errors[] = 'Name is required';
}
if ( empty($errors) ) {
return;
}
throw new ValidationException($errors);
}

public function createNewContact( $name, $phone, $email, $address ) {
try {
$this->openDb();
$this->validateContactParams($name, $phone, $email, $address);
$res = $this->contactsGateway->insert($name, $phone, $email, $address);
$this->closeDb();
return $res;
} catch (Exception $e) {
$this->closeDb();
throw $e;
}
}

public function deleteContact( $id ) {
try {
$this->openDb();
$res = $this->contactsGateway->delete($id);
$this->closeDb();
} catch (Exception $e) {
$this->closeDb();
throw $e;
}
}

}

ContactsService object does not work with the database directly; instead it uses the ContactsGateway object which in return issues queries to the database. This technique is called Table Data Gateway.

The source

First let’s look at the index.php source:


<?php

require_once 'controller/ContactsController.php';

$controller = new ContactsController();

$controller->handleRequest();

?>

This script has simple role, it instantiates the controller object and hands it control over the application via the handleRequest method.

All requests must go through the index.php script because MVC requires that all requests are handled by the controller which is called from the index.php script. Web MVC applications usually redirects all requests to go through the index.php which can be done in server configuration.

Let’s look at the handleRequest method of the controller.

class ContactsController {

...

     public function handleRequest() {

        $op = isset($_GET['op'])?$_GET['op']:NULL;

        try {

            if ( !$op || $op == 'list' ) {

                $this->listContacts();

            } elseif ( $op == 'new' ) {

                $this->saveContact();

            } elseif ( $op == 'delete' ) {

                $this->deleteContact();

            } elseif ( $op == 'show' ) {

                $this->showContact();

            } else {

                $this->showError("Page not found", "Page for operation ".$op." was not found!");

            }

        } catch ( Exception $e ) {

            $this->showError("Application error", $e->getMessage());

        }

    }

...

}

The handleRequest method acts as a dispatcher for the actions. The method decides which action should it invoke based on a value of the HTTP GET “op” parameter and invokes the method that implements the action. If any exception is thrown from the action methods the handleRequest method catches them and prints the error message.

Now, let’s look at the action methods:


class ContactsController {

private $contactsService = NULL;

public function __construct() {
$this->contactsService = new ContactsService();
}

public function redirect($location) {
header('Location: '.$location);
}

public function handleRequest() {
$op = isset($_GET['op'])?$_GET['op']:NULL;
try {
if ( !$op || $op == 'list' ) {
$this->listContacts();
} elseif ( $op == 'new' ) {
$this->saveContact();
} elseif ( $op == 'delete' ) {
$this->deleteContact();
} elseif ( $op == 'show' ) {
$this->showContact();
} else {
$this->showError("Page not found", "Page for operation ".$op." was not found!");
}
} catch ( Exception $e ) {
// some unknown Exception got through here, use application error page to display it
$this->showError("Application error", $e->getMessage());
}
}

public function listContacts() {
$orderby = isset($_GET['orderby'])?$_GET['orderby']:NULL;
$contacts = $this->contactsService->getAllContacts($orderby);
include 'view/contacts.php';
}

public function saveContact() {

$title = 'Add new contact';

$name = '';
$phone = '';
$email = '';
$address = '';

$errors = array();

if ( isset($_POST['form-submitted']) ) {

$name       = isset($_POST['name']) ?   $_POST['name']  :NULL;
$phone      = isset($_POST['phone'])?   $_POST['phone'] :NULL;
$email      = isset($_POST['email'])?   $_POST['email'] :NULL;
$address    = isset($_POST['address'])? $_POST['address']:NULL;

try {
$this->contactsService->createNewContact($name, $phone, $email, $address);
$this->redirect('index.php');
return;
} catch (ValidationException $e) {
$errors = $e->getErrors();
}
}

include 'view/contact-form.php';
}

public function deleteContact() {
$id = isset($_GET['id'])?$_GET['id']:NULL;
if ( !$id ) {
throw new Exception('Internal error.');
}

$this->contactsService->deleteContact($id);

$this->redirect('index.php');
}

public function showContact() {
$id = isset($_GET['id'])?$_GET['id']:NULL;
if ( !$id ) {
throw new Exception('Internal error.');
}
$contact = $this->contactsService->getContact($id);

include 'view/contact.php';
}

public function showError($title, $message) {
include 'view/error.php';
}

}

First, we have the listContacts method which has a simple workflow, it reads a parameter required for sorting the contacts, gets sorted contacts from the model, stores it in the contacts variable and, finally, includes the view.

Second, we have the deleteContact method which reads an id of the contact and tells the model to delete it. Finally, it redirects the user back to the index.php script which in return invokes the list contacts action. The delete contact action can not work without the id parameter, so, the method will throw an exception if the id of a contact is not set.

Third method is the showContact method which is similar to the deleteContact method so I will not waste space explaining it.

Finally, the saveContact method displays the “Add new contact” form, or, processes the data passed from the form if it was submitted. If any ValidationException occurred in the model, errors are collected from the exception and passed to the view. Ideally the view should display it (and indeed it does, as you will see soon).

Now let’s look at some views, first I want to show you the contacts.php view which is straightforward:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Contacts</title>
<style type="text/css">
table.contacts {
width: 100%;
}

table.contacts thead {
background-color: #eee;
text-align: left;
}

table.contacts thead th {
border: solid 1px #fff;
padding: 3px;
}

table.contacts tbody td {
border: solid 1px #eee;
padding: 3px;
}

a, a:hover, a:active, a:visited {
color: blue;
text-decoration: underline;
}
</style>
</head>
<body>
<div><a href="index.php?op=new">Add new contact</a></div>
<table border="0" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th><a href="?orderby=name">Name</a></th>
<th><a href="?orderby=phone">Phone</a></th>
<th><a href="?orderby=email">Email</a></th>
<th><a href="?orderby=address">Address</a></th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody>
<?php foreach ($contacts as $contact): ?>
<tr>
<td><a href="index.php?op=show&id=<?php print $contact->id; ?>"><?php print htmlentities($contact->name); ?></a></td>
<td><?php print htmlentities($contact->phone); ?></td>
<td><?php print htmlentities($contact->email); ?></td>
<td><?php print htmlentities($contact->address); ?></td>
<td><a href="index.php?op=delete&id=<?php print $contact->id; ?>">delete</a></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body>
</html>

The contacts.php view, which is used by the list contacts action, requires the contacts variable to be filled with contact objects. The variable is filled in the listContacts method and passed to the view using the PHP scoping rules and then the view uses the data from it to display the contacts as an HTML table.

Scripts like the contacts.php script are called templates. With a template developer defines a fixed structure that is filled with variable values ​ ​from the application (at run-time) and then presented to the user. Templates contain only presentation logic, because of this, they are understandable by non-programmers (designers, …) which makes them quite usefull and frequently used in web applications.

Now let’s take a look at more complex view, the contact-form.php view:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>
<?php print htmlentities($title) ?>
</title>
</head>
<body>
<?php
if ( $errors ) {
print '<ul>';
foreach ( $errors as $field => $error ) {
print '<li>'.htmlentities($error).'</li>';
}
print '</ul>';
}
?>
<form method="POST" action="">
<label for="name">Name:</label><br/>
<input type="text" name="name" value="<?php print htmlentities($name) ?>"/>
<br/>

<label for="phone">Phone:</label><br/>
<input type="text" name="phone" value="<?php print htmlentities($phone) ?>"/>
<br/>
<label for="email">Email:</label><br/>
<input type="text" name="email" value="<?php print htmlentities($email) ?>" />
<br/>
<label for="address">Address:</label><br/>
<textarea name="address"><?php print htmlentities($address) ?></textarea>
<br/>
<input type="hidden" name="form-submitted" value="1" />
<input type="submit" value="Submit" />
</form>

</body>
</html>

This view, also an template, is used by the add contact action and it displays the “Add new contact” form to the user. All variables required by the template are filled in the saveContact action method including the errors variable which, if set, is used by the template to display errors that occurred in the model while trying to create a new contact so the user can fix invalid data causing them.

This template shows us usefulness of controller-view separation, because, without it we would put controller logic from the saveContact method into the contact-form.php script. I say script because if a template contains any controller logic in it, it is no more a template.

Conclusion

I hope you understood the MVC and that you i it, try to implement some sample application using your own simple MVC framework, but do not reinvent the wheel because there are many excellent MVC frameworks out there, for any programming language. Just google it up.

As always if you have any questions or suggestions don’t hesitate to write them in the comment form below.

Download Complete Project:

Hi, My name is Masud Alam, love to work with Open Source Technologies, living in Dhaka, Bangladesh. I graduated in 2009 with a bachelor’s degree in Engineering from State University Of Bangladesh, I’m also a Certified Engineer on ZEND PHP 5.3, I served my first five years a number of leadership positions at Winux Soft Ltd, SSL Wireless Ltd, CIDA and MAX Group where I worked on ERP software and web development., but now i’m a co-founder and Chief Executive Officer and Managing Director of TechBeeo Software Consultancy Services Ltd. I’m also a Course Instructor of ZCE PHP 5.3 Certification and professional web development course at IBCS-PRIMAX Software (Bangladesh) Limited – a leading Training Institute in the country.
Print Friendly
38 comments on “CRUD with PHP OOP and MVC Design Pattern
    • I believe that the author can do that, but as he said, he wanted to be quick about the creation of the model. The whole point was to explain MVC.

  1. very very helpful tutorial for beginners using MVC pattern, but quiet difficult because i dont work on OOPS .. Thanks a lot

  2. I 2nd that, please include an update section of the this simple CRUD. I working on adding one now but am stuck with how to pass in the data for $contacts->name, $contacts->phone,$contacts->email and $contacts->address on the edit form page. Any suggestions?

  3. NVM, finally got it… took me awhile to understand the workflow on how you were pulling in the templates from the controller. I was instead trying to edit the template with code logic in it instead of driving it from the controller. I ended up creating another method for edit obviously and another method to show the fields for show contact. There is probably a better way to do this, but it finally worked……….. 🙂

  4. great tutorial…

    if we want to add .htaccess file for this project, how to do it?
    I’m so glad if you want to reply, I want to learn more about MVC design pattern concept before I move and learn several frameworks.

    thanks 🙂

  5. Great tutorial, it’s so usefull…

    I’ve problem, maybe you or any body can give an advice, I want to include some css files, but always not working, when i look page source, then i check the path, i get “page not found” response. Maybe there are many way to include css files..?

    regards..

  6. I still don’t understand the benefit of using “Table Data Gateway” pattern with the use of ContactsService class. To me, it seems quite redundant because you can interact directly between ContactsController and ContactsGateway. Pls elaborate.

  7. Hey, it’s great tutorial..

    i’ve same problem with astro, that’s include a file *.css to html in folder view..
    In this tutorial still join with one file.. i want to join that file with bootstrap, but still not work..
    Maybe u have way to do that, or u can explain about how to do that, or any body can help me, i want to built my system with MVC design pattern..

    thanks a lot..

  8. Sorry, I’m a bit baffled. What actually is in the ContactsGateway.php file? You mentioned in the tutorial that you would explain it later, but unless I’ve missed something, there was nothing further.

  9. I’ve just noticed that there’s a link to download the source code. Apologies for missing it on the first read through – I’ll get the code now.

  10. dil khush kar diya bhai tune toh masha allah.
    i am JAva developer.
    Its a same style we code using servlet jsp.
    kindly send me your personal email id just to be in touch

  11. very usefull and easy to learn MVC. but how about update contact? on this example just only show.. not update.. i try to change this but failed. any information about this?

Leave a Reply

Your email address will not be published. Required fields are marked *