Introduction

In object-oriented programming development, model-view-controller (MVC) is the name of a methodology or design pattern for successfully and efficiently relating the user interface to underlying data models.

The MVC pattern has been heralded by many developers as a useful pattern for the reuse of object code and a pattern that allows them to significantly reduce the time it takes to develop applications with user interfaces.

The model-view-controller pattern has three main components or objects to be used in software development:

Requirements

Features of this MVC Framework

URL Routing

By default, a URL has a one-to-one relationship to the Controller and Method called which has the following format:

http://example.com/controller/method/param1/param2

In some instances, however, you may want to remap this relationship so that a different class/method can be called instead of the one corresponding to the URL. For example, let’s say you want your URLs to have this prototype:

example.com/product/1/
example.com/product/2/
example.com/product/3/
example.com/product/4/

Normally the second segment of the URL is reserved for the method name, but in the example above it instead has a product ID. To overcome routes allow you to remap the URI handler.

NOTE it is not a requirement that you pass all parameters in the URL. You can create URL routes having only a controller/method/ pattern and provide data via HTTP POST, for example, coming from a form.

You can add your custom routes to the routes configuration file located here: /app/config/routes.php

WILDCARDS
A typical wildcard route might look something like this:

$route['product/:num'] = 'catalog/product_lookup/$1';

In a route, the array key contains the URI to be matched, while the array value contains the destination it should be re-routed to. In the above example, if the literal word “product” is found in the first segment of the URL, and a number is found in the second segment, the “catalog” class and the “product_lookup” method are instead used.

You can match literal values or you can use two wildcard types: (:num) will match a segment containing only numbers. (:any) will match a segment containing any character (except for ‘/’, which is the segment delimiter).

REGULAR EXPRESSIONS
If you prefer you can use regular expressions to define your routing rules. Any valid regular expression is allowed, as are back-references. If you use back-references you must use the dollar syntax rather than the double backslash syntax.

A typical RegEx route might look something like this:

$route['products/([a-z]+)/(\d+)'] = '$1/id_$2';

In the above example, a URI similar to products/shirts/123 would instead call the “shirts” controller class and the “id_123” method.

NOTE:

IMPORTANT! Do not use leading/trailing slashes.

EXAMPLES:

$route['journals'] = 'blogs';

A URL containing the word “journals” in the first segment will be remapped to the “blogs” controller class.

$route['product/(:any)'] = 'catalog/product_lookup/$1';

A URL with “product” as the first segment, and anything in the second will be remapped to the “catalog” controller class and the “product_lookup” method.

$route['product/(:num)'] = 'catalog/product_lookup_by_id/$1';

A URL with “product” as the first segment, and a number in the second will be remapped to the “catalog” controller class and the “product_lookup_by_id” method passing in the match as a variable to the method.

Models

Models that you create must be stored in the /app/models/ directory and MUST use a CamelCase.php file naming format. The Class name MUST also be named identically as the file name like so:

class CameCase extends Model
{ .. }

The base Model serves as an abstract to PDO and can be extended by any custom Model. The base Model will handle all the heavy lifting to create a proper PDO database query and return results, if any.

The Base Model located here /app/core/Model.php can be extended by your custom models like so:

class User extends Model
{
    private $db;

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

    public function getUsers()
    {
        /* get all users */
        $results = $this->db->select("users");
    }

    public function getMaleUsers()
    {
        /* get a specific user */
        $results = $this->db->select("users", "Gender = 'male'");
    }

    public function addUser($fname, $lname, $age, $gender)
    {
        /* add a new user */
        $data = [
                "fname"  => $fname,
                "lname"  => $lname,
                "age"    => $age,
                "gender" => $gender
                ];
        $this->db->insert("users", $data);
    }
}

The Base Model class is fully commented providing all the standard Create, Read, Update, Delete functionality.

Important! The Base Model performs automatic mapping of table columns names to the key-names of the data array you pass into it. Any key-names not matching a table column name will be dropped.

Note There is an example User class model in the Models directory.

Views

Views are the presentation part of the MVC pattern and as such they define design/layout of the page. A typical View might look like the following:

Sometimes you may have reusable parts of your page such as a header and footer. The View Helper loads by default and allows you to "extend" your View. In this example, we are adding the common header and footer View fragments by specifying their location in the sub-directory called "common" within the Views directory, located here: /app/views/common/

extend_view(['common/header'], $data)
... the main body of your html page ...
extend_view(['common/footer'], $data)

The second optional parameter $data is used to pass an array collection of data to this view fragment.

load_style(['reset','main'])

The load_style() function will load a list of CSS style files located in your public directory here: /app/public/css/ *You do not need to specify the file extension ".css"

load_script(['main','other']);

The load_script() function will load a list of Javascript files located in your public directory here: /app/public/js/ *You do not need to specify the file extension ".js"

NOTE

extend_view(['reports/daily/common/header'], $data)
extend_view(['reports/weekly/common/header'], $data)

Controllers

To understand how Controllers work we need to back up a little bit and recall how we format a URL. For this example lets say we need to query information about a user and display the information on a report.

http://www.acme.com/user/report/123

Our URL could have this form, where we are requiring the "User" Controller class and passing into the "report" method the value of "123" (the user id).

Our Controller might look like the following:

class User extends Controller {

    // Load the Home Page if the URL does not specify a method
    public function index()
    {
        $this->view('home/index');
    }

    // Show our User Profile report
    public function report($user_id)
    {
        // Load the User Model so that we can query the database
        $user = $this->model('User');
        // Get this user
        $bind = [':id' => $user_id];
        $data = $user->select('users','id = :id', $bind);
        // Load the View passing to it the Users information as $data.
        $this->view('reports/user_profile', $data);
    }
}

Let's assume the record returned from the above query for the user had the following data:

['name' => 'Bob Smith', 'age' => '24', 'email' => 'bobsmith@example.com']

Within your View file you could access these values in one of two ways:

echo $data['name'] // outputs "Bob Smith"
echo $name // also outputs "Bob Smith"

Why offer both options? because developers have preferences on style.

Note:

* However, you may also use custom routing to hide a sub-directory. See the Routing section above.