Skip to content
rmasters edited this page Sep 13, 2010 · 2 revisions

This page walks through creating a sample application using php-mvc. Please be aware that this is a very early preview of the framework, and it’s not in any way complete. The current version (0.1) only implements the core MVC features, and some have known bugs (routing in particular is iffy). php-mvc also requires PHP 5.3.0 (Windows) – since it primarily relies on namespaces for file inclusion. If you’re still interested in trying it out here’s a sort of quickstart tutorial!

File structure

In this example we’ll be using a directory structure like so:

/ [document root]
  /application
    /controllers
      Index.php
    /views
      /Index
        index.php
      layout.php
    bootstrap.php
  /library
    /MVC
      [php-mvc src]
  /public
    index.php
    .htaccess

Once you have a directory structure like this checkout the contents of MVC from the repository into a corresponding MVC directory in your library path.

Bootstrapping the application

The first thing we’ll do after importing the library is to start bootstrapping the application. Open/create “bootstrap.php” in /application and add the following lines:

<?php
/**
 * Application bootstrapper
 */

define('APPLICATION_PATH', dirname(__FILE__));
define('APPLICATION_ENV', 'development');

/**
 * Load MVC file loader
 */
require '../library/MVC/Loader.php';
// Define autoloader
spl_autoload_register('\MVC\Loader::autoload');
// Set default path to libraries
\MVC\Loader::setLibraryPath(APPLICATION_PATH . '/../library');
// Set paths to controllers
\MVC\Loader::addPath('Controller', APPLICATION_PATH . '/controllers');
\MVC\Loader::addPath('Model', APPLICATION_PATH . '/models');

/**
 * Var registry
 */
$registry = \MVC\Registry::instance();

/**
 * Load layout
 */
$view = new \MVC\View('layout.php');
\MVC\View::setRegistry($registry);
$registry->view = $view;

/**
 * Cleanup
 */
unset($registry, $view);

This is a minimal bootstrapping file – a typical application will have a much longer one – implementing routes, connecting to a database, passing configuration values and more. Let’s walk through what we have in this example.

Application constants

At the start we define two constants: APPLICATION_PATH and APPLICATION_ENVIRONMENT. These are both used in our dispatcher for the front controller – which we’ll come to later. Essentially APPLICATION_PATH points to the location of /application and APPLICATION_ENVIRONMENT will be a string that helps configure different actions later on in the app (for example – which error messages to show).

File loader

The Loader component is the first class of the library we’ll meet. This is responsible for autoloading classes and interfaces from different libraries (not just MVC). One caveat to this is that the file location is based on namespaces – you can read more on that at the Loader page.

We firstly register our autoload implementation with the SPL handler. Next we add a default location for our libraries – /library. If you’re sharing libraries between applications this could be changed to another location, rather than one relative to the current application. Finally we add a few namespace-specific paths for Controllers and Models. Loader will automatically pick up anything in /application, but I’d rather store my controllers in “controllers” rather than “controller” – this is just a small rule for cosmetic reasons.

Instantiating the variable registry

Penultimately, we create an instance of the Registry pattern class for ease of storing variables. We could store variables statically using \MVC\Registry::set($name, $var); but creating an instance makes this a little easier to write – especially since the Registry is frequently used in the bootstrap loader.

Creating a default layout

Finally we create a View object to hold the base layout of the application. This will contain all the headers and design structure of the application – allowing for a consistent interface throughout the application. The layout view script will also do something special by importing another view script for the current controller/action. In our View scripts we use PHP’s short syntax for creating templates. It will likely help you to enable short_open_tags in your php.ini if you’d like less code. More information on using Views are available on the Views page.

Some cleanup

After all this prelude we need to cleanup the variables used since this file will be directly included into our dispatcher. This simply removes them from the local scope – preventing any accidents later on.

Creating the dispatcher

Now create /public/index.php. This file will handle nearly all requests made to our application. Here’s a sample code:

<?php
/**
 * Single-entry-point dispatcher
 */

try {
    require '../application/bootstrap.php';

    $registry = \MVC\Registry::instance();

    // Get controller
    $router = $registry->router;
    $router->getRoute();
    \MVC\Controller::setRegistry($registry);
    $controllerName = 'Controller\\' . $router->getController();
    $controller = new $controllerName;
    $actionMethod = $router->getAction() . 'Action';
    $controller->$actionMethod();

    // Output main layout
    $view = $registry->view;
    $view->output();
} catch(\MVC\Exception $e) {
    // library exception
    echo '<h2>Library Exception</h2>' .
         '<p>The MVC library has triggered an exception that has prevented this webpage from being completed.</p>';
    if (APPLICATION_ENV === 'development') {
        echo '<p>' . $e->getMessage() . '</p>' .
             '<p>' . $e->getFile() . ' (' . $e->getLine() . ')</p>';
    }
    echo str_replace("\n", '<br />', $e->getTraceAsString());
} catch(Exception $e) {
    // class exception (non user)
    echo '<h2>Exception</h2>' .
         '<p>The application has triggered an exception that has prevented this webpage from being completed.</p>';
    if (APPLICATION_ENV === 'development') {
        echo '<p>' . $e->getMessage() . '</p>' .
             '<p>' . $e->getFile() . ' (' . $e->getLine() . ')</p>';
    }
    echo str_replace("\n", '<br />', $e->getTraceAsString());
}

This is a rather hefty file – in a nutshell it:

  1. Includes the application bootstrapper.
  2. Uses the Router to get the current controller and action.
  3. Triggers the relevant action code for the controller (including some controller hooks).
  4. Outputs the layout view script loaded in the bootstrapper.

All this is done with a failsafe should any errors occur – two catch blocks to catch Exceptions thrown by the MVC library, and any other uncaught exceptions. These can be easily modified, and output different information based on what environment the application is running in. The messages above probably aren’t very informative to the end-user so it would be wise to customise them.

Routing requests to the dispatcher

You should configure your web-server to direct all requests to this file. The Router class implements two different methods of routing pages – a custom route that allows you to route specific URIs to controllers and actions and a default router that attempts to infer from the URI what the correct controller/action couplet is. More information on this is available on Routing.

Creating application code

Creating a controller

Let’s make the application do something. Create a file named Index.php in /application/controllers like this:

<?php

namespace Controller;

class Index extends \MVC\Controller
{
    public function indexAction() {
        self::$registry->view->message = 'Hello world!';
    }

    public function helloAction() {
        if (!isset($_GET['name'])) {
            self::$registry->view->name = 'anonymous stranger';
        } else {
            // obviously this is **unsafe** - proper filtering/validation is planned
            self::$registry->view->name = $_GET['name'];
        }
    }
}

It’s useful to note that the Index controller is the default controller called if no controller could be determined by the Router. Similarly “index” is the default action. (These names will be able to be customised in a later version).

Creating a layout

Create /application/views/layout.php and add the following code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
    <title>My App</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta http-equiv="Content-Style-Type" content="text/css" />
</head>
<body>
    <h1>My App</h1>
<?php
    /**
     * Where the magic happens! The layout view imports the active view.
     * View variables in the layout view are available to imported views.
     */
    $controller = self::$registry->router->getController();
    $action = self::$registry->router->getAction();
    $view = new \MVC\View("$controller/$action.php");
    $view->output();
?>
</body>
</html>

Action views

And finally two views for both of our actions:

Index action (/application/views/Index/index.php)

<p><?= $message ?></p>

Hello action (/application/views/Index/hello.php)

<p>Hello <?= $name ?></p>

Action!

Try visiting http://localhost/ in your browser (assuming you’re running php-mvc on your PC). You should see a simple “Hello World!” message. Try http://localhost/hello/name/Your-name for the hello action.

Conclusion

That’s the end of this tutorial – if this worked for you congratulations, you’ve somehow managed to get this cobbled together framework to do something! If not don’t be frustrated – limited testing has gone into this, chances are the problem is with the framework rather than your app. Soon a sample application will be available for examination (and hopefully a more in-depth tutorial).