Create a simple PHP MVC Framework

In this blog post we’ll create a simple PHP MVC Framework from scratch. This simple framework can be used as a tool for learning the basic plot behing MVC or you can modify it and make it grow into something bigger and much more useful.

MVC?

MVC stands for “Model View Controller”. MVC is a concept that enables us to separate our business logic from the presentation. One of the biggest advantages is the code reusability. There are other benefits, such as: We can let the designers edit the Views, without worrying that they will screw up database access logic.

I’m convinced. What do I need to begin?

For this tutorial you will need PHP/MySQL. You can get them in a variety of ways, but it’s much simpler to get a distribution that installs everything you need for you, such as XAMPP. Or get PHP/MySQL using Microsoft’s Web Platform Installer and run PHP on IIS.

Let’s get to it. We’ll create a PHP MVC framework and use it to create a simple news portal.

Setting up the Database

We’ll have a simple database with just two tables which will contain our news. You can create the MySQL database using phpMyAdmin or an SQL client, such as HeidiSQL, for example.

The basic plot is: One category can have many articles and article can’t be without a category, so we need to add a foreign key to reference categories in the articles table.

Foreign key also controls the changes in the primary key table. For example, we can’t delete a category from categories table if there is a article in the articles table that references the category, because that would compromise the integrity of our data.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TABLE `categories` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `category_name` varchar(150) NOT NULL,
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category` int(11) NOT NULL,
  `title` varchar(255) NOT NULL,
  `intro` text NOT NULL,
  `article` text NOT NULL,
  `date` datetime NOT NULL,
  `status` enum('Y','N') NOT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_articles_categories` (`category`),
  CONSTRAINT `FK_articles_categories` FOREIGN KEY (`category`) REFERENCES `categories` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Now use you SQL client to enter some sample data.

For example, look at the following screenshot. Because HeidiSQL understands that there is a foreign key constraint, it allows us to select the category from the categories table when entering data into articles table:

HeidiSQL understands forign key constraints

Once you create the tables, we need some data to play around with. You can just enter some data yourself, or Download Samples for this blog post which include some data.

Basic philosophy

We’ll set up a system where we can include the appropriate file based on url. As user goes through our site, we’ll use the appropriate controller, which is basically just a PHP file which uses our classes (models).

Controllers are the entry points. They say which model to use and which view to display. Models are the business logic. They get the data. We use views to display the data. It may sound complicated, but it’s really not. You’ll see.

Our URLs will look like this: http://simplemvc.local/?load=news/details/1. As you can see, we’ll use load parameter to tell what controller and view to use. In this example, we’ll use news controller and list and details views.

NOTE: We’ll make URLs look better later.

First Steps

First thing we need is to create a View class which will enable us to create PHP templates.

So, let’s demonstrate how to do that.

view.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
 
class View
{
    protected $_file;
    protected $_data = array();
     
    public function __construct($file)
    {
        $this->_file = $file;
    }
     
    public function set($key, $value)
    {
        $this->_data[$key] = $value;
    }
     
    public function get($key)
    {
        return $this->_data[$key];
    }
     
    public function output()
    {
        if (!file_exists($this->_file))
        {
            throw new Exception("Template " . $this->_file . " doesn't exist.");
        }
         
        extract($this->_data);
        ob_start();
        include($this->_file);
        $output = ob_get_contents();
        ob_end_clean();
        echo $output;
    }
}

This class is simple. When creating objects from this class we simply need to provide path to the Template file. Then we need to set the data we want to display in our template. When we set all the data we simply use the output() method which extracts the data from the array and uses output buffering to “fill” the placeholders in the template with values from the array.

How do I use the View class?

Simple. First, create a Template file and save it as simpletemplate.tpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
 
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title><?php echo $title; ?></title>
    </head>
    <body>
     
        <h1><?php echo $greeting; ?></h1>
         
    </body>
</html>

Then in a new php file do:

1
2
3
4
5
6
7
8
<?php
 
require 'view.php';
 
$view = new View('simpletemplate.tpl');
$view->set('title', 'This is a simple template file!');
$view->set('greeting', 'Hello, World');
echo $view->output();

Our template loaded.

I’m pretty sure you understand how cool is this! 🙂 So, let’s continue with our project!

Other stuff needed

Now we can continue with creating our little MVC project. First, we’ll need a good directory structure. Here is the final:

  • controllers
  • includes
  • utilities
  • models
  • views
    • news

Move the view.php file to utilities folder, we’ll need it to display an appropriate view.

Utilities

We also need is a simple class for connecting to the database. Indide utilities folder create a new file named db.php.

db.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
 
class Db
{
    private static $db;
     
    public static function init()
    {
        if (!self::$db)
        {
            try {
                $dsn = 'mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=UTF-8';
                self::$db = new PDO($dsn, DB_USER, DB_PASS);
                self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                self::$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
            } catch (PDOException $e) {
                die('Connection error: ' . $e->getMessage());
            }
        }
        return self::$db;
    }
}

You might say this step isn’t really needed, but I find it’s cool to have a database class which we can later use. Also what we got is a singleton class that limits class instantiation to one object.

Adding the basics

OK, now we have all the utilities we need for successfully connecting to the database and working with Templates. Let’s continue with adding the basics of our simple MVC project.

Create a new PHP file, named index.php. In here we’ll setup autoloading, and include basic files. I got defining DS and HOME constants from this article. It also helped to make my original autoloader a little better.

Our index.php looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
define ('DS', DIRECTORY_SEPARATOR);
define ('HOME', dirname(__FILE__));
 
ini_set ('display_errors', 1);
 
require_once HOME . DS . 'config.php';
require_once HOME . DS . 'utilities' . DS . 'bootstrap.php';
 
function __autoload($class)
{
    if (file_exists(HOME . DS . 'utilities' . DS . strtolower($class) . '.php'))
    {
        require_once HOME . DS . 'utilities' . DS . strtolower($class) . '.php';
    }
    else if (file_exists(HOME . DS . 'models' . DS . strtolower($class) . '.php'))
    {
        require_once HOME . DS . 'models' . DS . strtolower($class) . '.php';
    }
    else if (file_exists(HOME . DS . 'controllers' . DS . strtolower($class) . '.php'))
    {
        require_once HOME . DS . 'controllers'  . DS . strtolower($class) . '.php';
    }
}

We setup autoloading so correct file is included when we create a new object from a specified class.

config.php

As in some previous blog posts, the config.php file will contain the data needed to connect to the database, so this file is pretty simple.

1
2
3
4
5
<?php
define ('DB_HOST''localhost');
define ('DB_NAME''simplemvc');
define ('DB_USER''root');
define ('DB_PASS''');

bootstrap.php

After that comes our bootstrap.php file. Create it inside utilities folder, per index.php file. This one’s a bit more complicated, but I’m sure you’ll understand everything:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
$controller = "news";
$action = "index";
$query = null;
 
if (isset($_GET['load']))
{
    $params = array();
    $params = explode("/", $_GET['load']);
 
    $controller = ucwords($params[0]);
     
    if (isset($params[1]) && !empty($params[1]))
    {
        $action = $params[1];
    }
     
    if (isset($params[2]) && !empty($params[2]))
    {
        $query = $params[2];
    }
}
 
$modelName = $controller;
$controller .= 'Controller';
$load = new $controller($modelName, $action);
 
if (method_exists($load, $action))
{
    $load->$action($query);
}
else
{
    die('Invalid method. Please check the URL.');
}

If you don’t get it, here’s a simple explanation: First we setup default values for our controller, view and optional query string. So if there’s no load parameter, default controller is news, and default action (view) index.

Next, we create a new Controller object based on the supplied parameters.

1
2
3
$modelName = $controller;
$controller .= 'Controller';
$load = new $controller($modelName, $action);

So, by default it will be like we said:
$load = new NewsController(‘news’, ‘index’);

After that, we simply invoke a method which must be named like our $action parameter. By default that method will be “index()“. This is called Variable functions.

1
2
3
4
5
6
7
8
if (method_exists($load, $action))
{
    $load->{$action}($query);
}
else
{
    die('Invalid method. Please check the URL.');
}

Of course, if there’s no such method, we can’t continue.

Adding main stuff

First, we’ll add a new base Controller class from which all of our Controllers will inherit. This base class will set a default view and have a method to set a model, because we want to be able to set the model manually.

Controller base class

Inside controllers folder create a new controller.php file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
 
class Controller
{
    protected $_model;
    protected $_controller;
    protected $_action;
    protected $_view;
    protected $_modelBaseName;
     
    public function __construct($model, $action)
    {
        $this->_controller = ucwords(__CLASS__);
        $this->_action = $action;
        $this->_modelBaseName = $model;
         
        $this->_view = new View(HOME . DS . 'views' . DS . strtolower($this->_modelBaseName) . DS . $action . '.tpl');
    }
     
    protected function _setModel($modelName)
    {
        $modelName .= 'Model';
        $this->_model = new $modelName();
    }
     
    protected function _setView($viewName)
    {
        $this->_view = new View(HOME . DS . 'views' . DS . strtolower($this->_modelBaseName) . DS . $viewName . '.tpl');
    }
}

Not exactly nuclear science, huh? 🙂 This is simply a base class which enables us to create a new Model, and set a View using already known syntax.

The constructor sets a default view to /views/MODEL_BASE_NAME/ACTION_NAME, so, for example default view for a NewsController controller and a index action would be: /views/news/index.tpl. The Controller class also enables us to setup our own View via the _setView() method.

You also might notice that we don’t create an appropriate Model object. That’s deliberate. For instance, there might be a Controller which doesn’t use Models at all. And for those that do, we have a method to set the Model.

Model base class

Now, let’s create a same base class for our Models. Create a new file named model.php inside the models folder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
 
class Model
{
    protected $_db;
    protected $_sql;
     
    public function __construct()
    {
        $this->_db = Db::init();
    }
     
    protected function _setSql($sql)
    {
        $this->_sql = $sql;
    }
     
    public function getAll($data = null)
    {
        if (!$this->_sql)
        {
            throw new Exception("No SQL query!");
        }
         
        $sth = $this->_db->prepare($this->_sql);
        $sth->execute($data);
        return $sth->fetchAll();
    }
     
    public function getRow($data = null)
    {
        if (!$this->_sql)
        {
            throw new Exception("No SQL query!");
        }
         
        $sth = $this->_db->prepare($this->_sql);
        $sth->execute($data);
        return $sth->fetch();
    }
}

We simply created a wrapper around PHP PDO, so all our models that extend the Model class can use those methods easily.

Reading the articles

We’ll add a new class which inherits from the Model Class. This class will use methods from the base class to return an array with articles and the details of a single article.

Adding NewsModel

Inside the models folder create a new file: newsmodel.php. Remember, we setup autoloading to use lowercase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
 
class NewsModel extends Model
{
    public function getNews()
    {
        $sql = "SELECT
                    a.id,
                    a.title,
                    a.intro,
                    DATE_FORMAT(a.date, '%d.%m.%Y.') as date,
                    c.category_name
                FROM
                    articles a
                INNER JOIN
                    categories AS c ON a.category = c.category_id
                ORDER BY a.date DESC";
         
        $this->_setSql($sql);
        $articles = $this->getAll();
         
        if (empty($articles))
        {
            return false;
        }
         
        return $articles;
    }
     
    public function getArticleById($id)
    {
        $sql = "SELECT
                    a.title,
                    a.article,
                    DATE_FORMAT(a.date, '%d.%m.%Y.') as date,
                    c.category_name
                FROM
                    articles a
                INNER JOIN categories AS c ON a.category = c.category_id
                WHERE
                    a.id = ?";
         
        $this->_setSql($sql);
        $articleDetails = $this->getRow(array($id));
         
        if (empty($articleDetails))
        {
            return false;
        }
         
        return $articleDetails;
    }
}

As you can see, this is a very simple class which has two methods. getVisibleNews() method simply returns all the Articles that are active. getArticleById($id) method accepts an $id parameter which specifies the ID of the article to be returned.

Adding the controller

OK, that’s cool. Now we need to create a new Controller which will use the model to get the data and then load the appropriate view. Remember the index.php file? We said there that our default controller will be NewsController. Create a new file named newscontoller.php inside a controllers folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
 
class NewsController extends Controller
{
    public function __construct($model, $action)
    {
        parent::__construct($model, $action);
        $this->_setModel($model);
    }
     
    public function index()
    {
        try {
             
            $articles = $this->_model->getNews();
            $this->_view->set('articles', $articles);
            $this->_view->set('title', 'News articles from the database');
             
            return $this->_view->output();
             
        } catch (Exception $e) {
            echo "Application error:" . $e->getMessage();
        }
    }
}

So, we have a basic controller with one default action -> index. Just like we said in our bootstrap.php file. If we try to run our little application now we’ll get an Exception, because one thing’s missing. Yes, you got that right – our template file (View). So, let’s create it.

Default view

If you followed this blog post from the beginning you have a news folder inside your views folder. That’s because our controller can have lots of actions and almost every action can have it’s view.

Now inside includes folder create a file named menu.inc.php.

1
2
3
4
5
6
<nav>
    <ul id="menu">
        <li><a href="/">Home</a></li>
        <li><a href="/contact/index">Contact</a></li>
    </ul>
</nav>

We’ll include this menu in our template files.

Because we’re using a default view for this situation, we’ll create a new file named index.tpl inside the /views/news folder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
 
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title><?php echo $title; ?></title>
    </head>
    <body>
     
        <?php include HOME . DS . 'includes' . DS . 'menu.inc.php'; ?>
         
        <h1>News articles</h1>
 
        <?php
            if ($articles):
            foreach ($articles as $a): ?>
 
            <article>
                <header>
                    <h1><a href="/news/details/<?php echo $a['id']; ?>"><?php echo $a['title']; ?></a></h1>
                    <p><?php echo $a['category_name']; ?></p>
                    <p>Published on: <time pubdate="pubdate"><?php echo $a['date']; ?></time></p>
                </header>
                <p><?php echo $a['intro']; ?></p>
                <p><a href="/news/details/<?php echo $a['id']; ?>">Continue reading</a></p>
                <hr/>
            </article>
        <?php
            endforeach;
            else: ?>
 
        <h1>Welcome!</h1>
        <p>We currently do not have any articles.</p>
 
        <?php endif; ?>
         
    </body>
</html>

Now save the file and reload the site. You should get something similar to this:

List of articles from database

If you try to click on a link you’ll notice the links don’t work. Well, that’s because we need a URL Rewrite to map those pretty URLs to our load parameter from bootstrap.php file. Remember? 🙂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (isset($_GET['load']))
{
    $params = array();
    $params = explode("/", $_GET['load']);
 
    $controller = ucwords($params[0]);
     
    if (isset($params[1]) && !empty($params[1]))
    {
        $action = $params[1];
    }
     
    if (isset($params[2]) && !empty($params[2]))
    {
        $query = $params[2];
    }
}

URL Rewriting

So for those pretty URLs to work we need to create a .htaccess (if you’re running XAMPP, LAMPP or something similar) or web.config (if you’re on IIS) file. So, go right ahead and grab the code you need:

.htaccess

1
2
3
4
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?load=$1 [PT,L]

web.config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="MVCRewrite" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.php?load={R:1}" appendQueryString="false" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Wohoo! Now the URLs work, but we still need a way to display the Article Details. That’s right. We don’t have a details action in our News controller.

Article Details

Open our newscontroller.php and add a details() method so our controller now looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
 
class NewsController extends Controller
{
    public function __construct($model, $action)
    {
        parent::__construct($model, $action);
        $this->_setModel($model);
    }
     
    public function index()
    {
        try {
             
            $articles = $this->_model->getVisibleNews();
            $this->_view->set('articles', $articles);
            $this->_view->set('title', 'News articles from the database');
             
            return $this->_view->output();
             
        } catch (Exception $e) {
            echo "Application error:" . $e->getMessage();
        }
    }
     
    // Add THIS
    public function details($articleId)
    {
        try {
             
            $article = $this->_model->getArticleById((int)$articleId);
             
            if ($article)
            {
                $this->_view->set('title', $article['title']);
                $this->_view->set('articleBody', $article['article']);
                $this->_view->set('datePublished', $article['date']);
            }
            else
            {
                $this->_view->set('title', 'Invalid article ID');
                $this->_view->set('noArticle', true);
            }
             
            return $this->_view->output();
              
        } catch (Exception $e) {
            echo "Application error:" . $e->getMessage();
        }
    }
    // End
}

So, there’s just one thing missing: The View. So, let’s add that.

Inside a /views/news folder create a new file: details.tpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
 
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title><?php echo $title; ?> | Article Details</title>
    </head>
    <body>
     
        <?php include HOME . DS . 'includes' . DS . 'menu.inc.php'; ?>
         
        <article>
            <header>
                <h1><?php echo $title; ?></h1>
                <p>Published on: <time pubdate="pubdate"><?php echo $datePublished; ?></time></p>
            </header>
            <p>
                <?php echo $articleBody; ?>
            </p>
        </article>
         
        <a href="/">Back to article list</a>
         
    </body>
</html>

If you check the details page now, you’ll see that it works and we see the article details.

Article details

Simple contact form

Now let’s create a simple Contact form that saves info into the database.

First thing we need is, of course, the contact table in the database. Let’s add that:

1
2
3
4
5
6
7
8
9
CREATE TABLE `contact` (
    `id` INT(10) NOT NULL AUTO_INCREMENT,
    `first_name` VARCHAR(50) NULL,
    `last_name` VARCHAR(50) NULL,
    `email` VARCHAR(50) NOT NULL,
    `message` TEXT NOT NULL,
    PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'

Next, we’ll need a controller. Create a new file named contactcontroller.php inside the controllers folder.

1
2
3
4
5
6
7
8
9
10
<?php
 
class ContactController extends Controller
{
    public function index()
    {
        $this->_view->set('title', 'Simple site Contact Form');
        return $this->_view->output();
    }
}

This method will simply return the form once user visits /contact url. Now we need a model to handle saving the data to database.

Contact model

Inside the models folder create a new file named contactmodel.php. This class will handle saving the data to the database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
 
class ContactModel extends Model
{
    private $_firstName;
    private $_lastName;
    private $_email;
    private $_message;
     
    public function setFirstName($firstName)
    {
        $this->_firstName = $firstName;
    }
     
    public function setLastName($lastName)
    {
        $this->_lastName = $lastName;
    }
     
    public function setEmail($email)
    {
        $this->_email = $email;
    }
     
    public function setMessage($message)
    {
        $this->_message = $message;
    }
     
    public function store()
    {
        $sql = "INSERT INTO contact
                    (first_name, last_name, email, message)
                VALUES
                    (?, ?, ?, ?)";
         
        $data = array(
            $this->_firstName,
            $this->_lastName,
            $this->_email,
            $this->_message
        );
         
        $sth = $this->_db->prepare($sql);
        return $sth->execute($data);
    }
}

This is a simple class. We have set methods for our form data, and a store() method to save the data.

Save method in the controller

Let’s save form input. Add this method to the ContactController class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public function save()
{
    if (!isset($_POST['contactFormSubmit']))
    {
        header('Location: /contact/index');
    }
     
    $errors = array();
    $check = true;
         
    $firstName = isset($_POST['first_name']) ? trim($_POST['first_name']) : NULL;
    $lastName = isset($_POST['last_name']) ? trim($_POST['last_name']) : NULL;
    $email = isset($_POST['email']) ? trim($_POST['email']) : "";
    $message = isset($_POST['message']) ? trim($_POST['message']) : "";
         
    if (empty($email))
    {
        $check = false;
        array_push($errors, "E-mail is required!");
    }
    else if (!filter_var( $email, FILTER_VALIDATE_EMAIL ))
    {
        $check = false;
        array_push($errors, "Invalid E-mail!");
    }
         
    if (empty($message))
    {
        $check = false;
        array_push($errors, "Message is required!");
    }
 
    if (!$check)
    {
        $this->_setView('index');
        $this->_view->set('title', 'Invalid form data!');
        $this->_view->set('errors', $errors);
        $this->_view->set('formData', $_POST);
        return $this->_view->output();
    }
         
    try {
                 
        $contact = new ContactModel();
        $contact->setFirstName($firstName);
        $contact->setLastName($lastName);
        $contact->setEmail($email);
        $contact->setMessage($message);
        $contact->store();
                 
        $this->_setView('success');
        $this->_view->set('title', 'Store success!');
                 
        $data = array(
            'firstName' => $firstName,
            'lastName' => $lastName,
            'email' => $email,
            'message' => $message
        );
                 
        $this->_view->set('userData', $data);
                 
    } catch (Exception $e) {
        $this->_setView('index');
        $this->_view->set('title', 'There was an error saving the data!');
        $this->_view->set('formData', $_POST);
        $this->_view->set('saveError', $e->getMessage());
    }
 
    return $this->_view->output();
}

Now, we’ll need a View to display our form. Inside views folder create a new folder named contact. Then inside it create a file named index.tpl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
 
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title><?php echo $title; ?></title>
    </head>
    <body>
     
        <?php include HOME . DS . 'includes' . DS . 'menu.inc.php'; ?>
         
        <h1><?php echo $title; ?></h1>
         
        <?php
        if (isset($errors))
        {
            echo '<ul>';
            foreach ($errors as $e)
            {
                echo '<li>' . $e . '</li>';
            }
            echo '</ul>';
        }
         
        if (isset($saveError))
        {
            echo "<h2>Error saving data. Please try again.</h2>" . $saveError;
        }
        ?>
         
        <form action="/contact/save" method="post">
             
            <p>
                <label for="first_name">First Name:</label>
                <input value="<?php if(isset($formData)) echo $formData['first_name']; ?>" type="text" id="first_name" name="first_name" />
            </p>
             
            <p>
                <label for="last_name">Last Name:</label>
                <input value="<?php if(isset($formData)) echo $formData['last_name']; ?>" type="text" id="last_name" name="last_name" />
            </p>
             
            <p>
                <label for="email">* E-mail:</label>
                <input value="<?php if(isset($formData)) echo $formData['email']; ?>" type="text" id="email" name="email" />
            </p>
             
            <p>
                <label for="message">* Message:</label>
                <textarea name="message" id="message" rows="5" cols="50"><?php if(isset($formData)) echo $formData['message']; ?></textarea>
            </p>
             
            <input type="submit" name="contactFormSubmit" value="Send" />
        </form>
         
    </body>
</html>

Form looks like this:

Contact form

As you can see in the save method, we’ll also need a success view. This one’s pretty simple:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
 
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title><?php echo $title; ?></title>
    </head>
    <body>
     
        <?php include HOME . DS .  'includes' . DS . 'menu.inc.php'; ?>
         
        <h1><?php echo $title; ?></h1>
        <h2>Data stored:</h2>
         
        <?php if (!empty($userData['firstName'])): ?>
            <h3>First Name:</h3>
            <p><?php echo $userData['firstName']; ?></p>
        <?php endif;?>
         
        <?php if (!empty($userData['lastName'])): ?>
            <h3>Last Name:</h3>
            <p><?php echo $userData['lastName']; ?></p>
        <?php endif;?>
         
        <h3>E-mail:</h3>
        <p><?php echo $userData['email']; ?></p>
         
        <h3>Message:</h3>
        <?php echo $userData['message']; ?>
         
    </body>
</html>

If you submit the form and got a success view, everything worked!

Contact form data is stored into the database

Conclusion

That’s it. We created a simple MVC framework which you can easily extend to meet your needs.

Article you might want to visit is: Write your own PHP MVC Framework. This article is awesome and it’s the one you should visit next. From there I got defining DS and HOME.

Advertisements
By Rz Rasel Posted in Php

3 comments on “Create a simple PHP MVC Framework

  1. Hi, I really like your tutorial. It’s just enough to get me started. I think I may be having an issue with mod_rewrite. I have a test server up and put your tutorial files at http://localhost/mvc. When I go there in my browser it works great. The problem is with all the links. Every link goes to http://localhost/wherever instead of http://localhost/mvc/wherever. I can manually type the /mvc/ directory into the address and it works fine, but I shouldn’t have to do that. I’ve been trying all sorts of stuff with the mod_rewrite, but nothing has worked. Could you help give me some direction?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s