nemetral.net | Insightful posts on design and code

August 13, 2008 · Category Patterns · icon16

A gentle introduction to MVC (part 3)

This article was written by nemetral.

Voices matter! Please feel free to share your opinion, ask for more explanations or point out divergences using comments.

No time to read this now? Bookmark it and come back later..

In part 1, we decoupled a vanilla PHP script and separated the business logic from the presentation. In part 2, we added a third element in the mix called the model and dedicated to the data access layer. It is now time to spice up the coding by introducing the role of Object-Oriented Programming in a MVC architecture and release the very basis (for educational purpose only) of a MVC framework.

Part 3: OOPs I’ve added objects

As you all know, OOP is famous for wrapping functions and variables into higher-level elements called objects (if you’re not familiar with OOP, I suggest you have a look at the Wikipedia page and some tutorials). So here is how we could merge MVC and OOP for a greater code flexibility, reusability and maintainability:

  • the controller is an object: in CodeIgniter for example, the first segment of the URL is the name of the controller’s object and the second segment is the name of the controller’s method which needs to be run (for example, calling http://community.com/members/show would trigger the instantiation of the members object and the call of the show method)
  • the model is an object: as a representation and abstraction of the data, the model is also an object (let’s call it members_model) and contains functions such as getAllMembers() for example
  • the view can be an object too: with general functions such as getTemplate() which selects the corresponding template or renderHTMLPage() which fills the HTML page with the data and outputs the result; but the templates (and sub-templates) are plain PHP/HTML files (let’s call the layout template members_view.php)
  • the router is a plain vanilla PHP file which essentially checks the validity of the request and loads the correct controller

Now if we translate these principles into actual PHP code, here is what a very raw MVC framework could look like (directly inspired from CodeIgniter):
Tree structure:

index.php
   /controllers
      members.php
   /models
      members_model.php
   /views
      members_view.php
      /tpl
         header.php

file: index.php

<?php 

$request = preg_replace("|/*(.+?)/*$|", "\\1", $_SERVER['PATH_INFO']);
$uri = explode('/', $request);

include 'controllers/' . $uri[0] . '.php';
$controller = new $uri[0];
$controller->{$uri[1]}();

?>
  1. index.php is called in the URL
  2. takes the PATH_INFO (here: /members/show/) and cleans the starting and trailing slashes using a REGEX
  3. explodes the URL into $uri[0] (name of the requested controller) and $uri[1] (name of the requested method)
  4. instanciates the controller and calls the method

file: controllers/members.php

<?php

class members
{
  function show() {
    include 'models/members_model.php';
    $model = new members_model();
    $members = $model->getAllMembers();

    include 'views/members_view.php';
  }
}

?>
  1. to simplify, there is no constructor and the function show() is directly run
  2. the method show() loads and instanciates the correct model, calls the method to get all the members and fills the $members variable with the data
  3. then the method show() loads the correct view: the latter is automatically parsed (the data contained in the $members variable is inserted in-between HTML tags) and output

file: models/members_model.php

<?php

class members_model
{
   function members_model() {
     $connection = mysql_connect('host', 'user', 'password');
     mysql_select_db('database', $connection);
   }

   function getAllMembers() {
     $data = mysql_query('SELECT name FROM members');
     $members = array();
     while ($item = mysql_fetch_array($data)) {
       $members[] = $item['name'];
     }
     return $members;
   }
}
  1. there is a constructor establishing once for all the connection to the database
  2. the method getAllMembers() queries the database, extracts the members’ names and returns them through an array

file: views/members_view.php

<html>
  <?php include 'views/tpl/header.php'; ?>
  <h1>Members of community.com:</h1>
  <ul>
  <?php for ($i = 0; $i < count($members); $i++) : ?>
    <li>Member #<?php echo $i + 1; ?>: <?php echo $members[$i]; ?></li>
  <?php endfor; ?>
  </ul>
</html>

file: views/tpl/header.php

<head>
  <title>Community.com rocks!</title>
</head>
  1. the main layout members_view.php is included by the controller and therefore automatically parsed
  2. the layout includes a reusable template for the header

Note: this code is for educational purpose only.

As you can see, building a OO MVC architecture is not that difficult. Please bear in mind that the code above is a basic way of achieving a MVC architecture and a lot of things could be improved:

  • first thing to do is to dramatically enhance the router file: there is no checking on whether the requested controller exists or not, and is accessible or not (this is a huge security hole)
  • instead of getting connected to the database in the model’s constructor, why not creating a model superclass managing the database connection and make each model an extension of this superclass? This way we could use several models from our controller without worrying about being already connected to the database or not
  • in the same way, why not creating a controller superclass with common features and make each controller an extension of this superclass?

Now CodeIgniter has all that.. and much more. It is secure, lightweight and brillantly coded. If you want to start coding MVC, CodeIgniter is a must have!

Entries (RSS) Did you enjoy this post? Consider subscribing to the RSS feed!

15 comments · 1 pingback · Leave yours

  1. Pingback : http://php.coding-school.com/php-code-2008-08-13-075523/
  2. karren
    October 30th, 2008
    1:11 am

    Hi!

    Thanks for this tutorial! I’m really happy you’ve shared this knowledge.

    Thanks!

  3. Maicon
    January 30th, 2009
    8:20 pm

    Well…very amazing…

    How is the division of archives of image, js, css?

    Thanks..

  4. nemetral
    January 30th, 2009
    11:25 pm

    @Maicon: images, js and css all belong to the Views (same level as HTML) so you can insert them in subfolders inside your Views folder. But there is better: you can also put them at the root of your website so as to shorten include paths (like http://example.com/img/image.jpg instead of http://example.com/engine/views/img/image.jpg). To be honest, images, css and js can be put wherever you want as long as they are correctly linked to.

  5. Scott
    February 4th, 2009
    8:26 pm

    So, okay, at what level of website complexity does this actually become useful? I mean, I make websites for small- to medium-sized businesses, which generally translates to small- to medium-sized sites, typically with no database integration. Is it fair to say that it’s not helpful to go to such extremes to create simple brochure sites?

  6. nemetral
    February 5th, 2009
    12:45 am

    @Scott: you are absolutely right. Each project deserves its own kind of coding. Brochure websites surely don’t need MVC. For blogs, Wordpress is surely what most people need (Wordpress is not MVC). This series of posts was rather aimed at explaining how to tackle a very specific project, with database integration, that doesn’t fit in any pre-thought types of websites; and how to tackle it using clean code.

  7. Scott
    February 5th, 2009
    5:35 pm

    Cool, thanks!

  8. Mike
    February 16th, 2009
    2:48 am

    It’s interesting that you have the index directly instantiate a class, instead of an interface. Perhaps not the best way to implement OOP. Do you have a class diagram of the example system that you’re describing? Not enough people design class diagrams before they try to design an MVC architecture, but upon designing one, alot of things become clear. I’d be interested to view a class diagram to gain a greater perspective on what you’re trying to describe ;)

  9. nemetral
    February 17th, 2009
    11:26 pm

    @Mike: this 3rd post about MVC is simply aimed at detailing how to put together a very raw and very basic MVC workflow. In this example, I wanted to emphasize the role of a router instantiating classes based on requested URLs: that’s what index.php does. But as explained in the conclusion, this example is simply an illustration of the workflow and is not secure.

  10. Maicon
    February 25th, 2009
    10:32 pm

    MemberController in class, as if I would not want to include ‘view/members_view.php’;, do this so automatically, any suggestions? Thanks

  11. nemetral
    February 26th, 2009
    11:02 pm

    @Maicon: sorry I don’t quite understand your question there. Could you explain a little more? Cheers.

  12. Maicon
    February 27th, 2009
    7:32 pm

    Ok, Sorry. I will go try again.

    In your example of mvc, the view (members_view.php) file was included in the class membersController, see below:

    class membersController {
    // action

    include ‘view/members_view.php’;
    }

    How I remove this include? see below:

    class membersController {
    function members {
    $members = $model->getAllMembers();
    }

    //no file include
    }

    members_view.php

    Thank you very much!

  13. Maicon
    February 27th, 2009
    7:41 pm

    Full Example:

    class membersController {
    function members() {
    $members = $this->model->getAllMembers();
    }

    // no file de include the view
    }

    membersView.php

    I tried to do using the function _autoload(),
    but without success.

  14. nemetral
    March 15th, 2009
    5:19 pm

    @Maicon: the include there is simply aimed at sending the HTML output to the browser once filled with the variables. I don’t exactly understand why including the view there bugs you at all. A lot of MVC frameworks have special methods to send the HTML to the browser but these are just abstractions of a plain old include.. Here I wanted to KISS so I let the include function in the example. If you really want to include the view file outside of the class then you could work out an inclusion in the very first “index.php” file but that’s not supposed to be its place at all!

  15. Juma929
    March 27th, 2009
    6:59 pm

    Hello,

    Love the advice etc you give here. Not sure I fully understand what Mike meant by instantiating an interface instead of a class, could you clarify?

    Thanks!

  16. misterm
    April 29th, 2009
    3:32 pm

    A very good article. I’ve enjoyed reading it. It would have been helpfull to have had a download link with the example files on page 3 (instead of having to copy and paste it). It makes for a great piece of code to start experimenting with.

Leave a comment