simple-mvc

Simple MVC [VC] framework to realize a test-driven experience.

View project onGitHub

Welcome to simple-mvc.

simple-mvc is a simple and full stack push & pull MVC framework for PHP 5.3+. The project (See docs here) is heavly inspired to different PHP microframeworks and ZF1 framework. The main goal of this framework is create a test-driven experience. The second aspect is to introducing people to PHP MVC frameworks. Getting started with this framework is very simple. First of all get the code using github or if you prefer use composer. The recommended way is through composer. Create a file named composer.json and add these lines

{
    "require": {
        "wdalmut/simple-mvc": "*"
    }
}

Now you can install the framework.

curl -s http://getcomposer.org/installer | php
php composer.phar install

Composer creates a vendor folder with simple-mvc framework.

Create the project using Composer

You can skip the base project creation (explained below [Getting started]) and create the base project with Composer.

$ curl -s http://getcomposer.org/installer | php
$ php composer.phar create-project wdalmut/simple-mvc-app my-project-name

Composer download and install into my-project-name folder simple-mvc and creates the base requirements:

  • public folders (web)
  • private folders (app)

In addition, it creates also a controller with associated view: the index page. You have only to add a virtual host that's point into web folder (See Create your Virtual Host section).

You can skip the Getting Started section.

Getting started (FULL STACK)

You have to create four folders. These folders separate all parts of your project.

  • controllers
  • views
  • layouts
  • public

The controllers folder contains your controllers, views your views (HTML files), layout the container of your views and the public that contains only the entry point of simple-mvc application, the .htaccess to force Apache webserver on this entry point and all of your static entries.

Create the entry point (index.php)

Into your public folder create a file named index.php. This file is the only requested by your webserver. The minimum configuration is the follow

<?php
require_once __DIR__ . '/../vendor/autoload.php';

$app = new Application();
$app->setControllerPath(__DIR__ . '/../controllers');

$app->bootstrap("view", function(){
    $view = new View();
    $view->setViewPath(__DIR__ . '/../views');

    return $view;
});

$app->bootstrap("layout", function(){
    $layout = new Layout();
    $layout->setViewPath(__DIR__ . '/../layouts');

    return $layout;
});

$app->run();

Always into your public folder create another file named .htaccess and add this content:

RewriteEngine  On
RewriteCond  %{REQUEST_FILENAME}  -s  [OR]
RewriteCond  %{REQUEST_FILENAME}  -l  [OR]
RewriteCond  %{REQUEST_FILENAME}  -d
RewriteRule  ^.*$  -  [NC,L]
RewriteRule  ^.*$  index.php  [NC,L]

The .htaccess use the Rewrite module of your Apache webserver (a2enmod rewrite to turn it on) and force the index.php file execution if a resource is missing.

Controllers

Now you have to create a new file IndexController.php into controllers folder

<?php
class IndexController extends Controller
{
    public function indexAction()
    {
        $this->view->helloText = "Hello World";
    }
}

The View

Now we have to create a view for the indexAction. View system search for a view using the controller name, in this case index, as a folder and the action name, in this case index too, as a filename. For that reason you have to create a folder named index into your views and inside this folder create a file named index.phtml. The content of index.phtml should be like

<p>
    <?php echo $this->helloText; ?>
</p>

As you can see the view doesn't contains the HTML page because is a role of the layout system to encapsulating views.

The Layout

For that reason create a file named layout.phtml into your layouts folder. The content should be like this:

<html>
    <head>
        <title>simple-mvc :: hello world</title>
    </head>
    <body>
        <?php echo $this->content; ?>
    </body>
</html>

Create your Virtual Host

Ok, now we are ready to see simple-mvc. You have only to configure your Apache webserver that use the public folder as entry-point. Create a new virtual host

<VirtualHost *:80>
    ServerAdmin dev@localhost
    DocumentRoot /path/to/your/project/public
    ServerName simple-mvc.local

    <Directory "/path/to/your/project/public">
        Options -Indexes FollowSymLinks
        AllowOverride all
        allow from all
    </Directory>
</VirtualHost>

Remember that you have to modify your hosts file adding the rule

127.0.0.1    simple-mvc.local

Restart your Apache webserver and go with your browser to http://simple-mvc.local/.

Route map to Controller-View

The router map your HTTP request to an action. simple-mvc router is pretty simple you can use only controller/action routes. For that reason if you get the page http://simple-mvc.local/tip/tap you are asking for TipController class that contains tapAction method. On the view side you have a folder named tip that contains tap.phtml file.

Controllers and views stack

You can push onto a stack another action during one action execution. This can be useful for stacking views. For doing this task simple call the then action helper.

<?php
// Controller
public function indexAction()
{
    // Action flow
    $this->then("/ctr/another");
}

The simple-mvc append the view of anotherAction() of CtrController class at bottom of index view. You can push actions continuously.

Header control

Headers are important. simple-mvc has a simple header manager. You can add headers that are emitted at the right time using the addHeader() method or you could remove all using clearHeaders() method.

<?php
public function textAction()
{
    //...
    $this->addHeader("Content-Type", "text/plain");
    //...

    // Clear all headers.
    $this->clearHeaders();
}

Redirect action helper

If you want to redirect you action to another using the redirect() method. The redirection use the 3xx series to redirect the client to another action. The method is really simple, you pass the destination path and the code for redirection (302 Temporary Redirect).

public function oneAction()
{
    $this->redirect("/act/b", 302);
}

You can skip the redirect code, the framework will send 301 automatically.

View and Layout management

Sometimes could be useful remove the attached view from a controller or disable the layout. Suppose that you want to produce a WS (webservice). The view is useless and the layout is completely useless. simple-mvc support layout and view suppression. See the XML example

<?php
class WsController extends Controller
{
    /**
     * <ping>pong</ping>
     */
    public function pingAction()
    {
        // Remove the attached view
        $this->setNoRender();
        // Remove the layout
        $this->disableLayout();

        // Create the XML
        $dom = new DOMDocument("1.0", "UTF-8");
        $element = $dom->createElement("ping", "pong");
        $dom->appendChild($element);

        // Show the XML
        echo $dom->saveXML();

        // Emit the right headers
        $this->addHeader("Content-Type", "text/xml");
    }
}

View helpers

simple-mvc support view helpers. In practice support two types of view helpers: view and layout helpers. The only difference is that view helpers are shared with layouts and not vice-versa. For that reason if you want to create a global helper append it into the view bootstrap operation otherwise append during layout bootstrap. A typical layout view helper is the title view helper, in few word a mechanism that allow an user to construct the page title during the application loop: starts with a title, an action add a title portion and so on. This kind of helper must be shared on layout and view because the title typically is in the layout view but modification is admitted in action views. A simple implementation of layout helper is:

<?php
$app->bootstrap('layout', function(){
    $layout = new Layout();
    $layout->setViewPath(__DIR__ . '/../layouts');

    // Add my app name
    $layout->title("My App");

    return $layout;
});

$app->bootstrap('view', function(){
    $view = new View();
    $view->addViewPath(__DIR__ . '/../views');

    // Append the view helper!
    $view->addHelper("title", function($part = false){
        static $parts = array();
        static $delimiter = ' :: ';

        return ($part === false) ? implode($delimiter, $parts) : $parts[] = $part;
    });

    return $view;
});

Inside your view you can append a title part.

<?php
// Action view (eg. index.phtml)
$this->title("Index Page");

Into your layout.phtml

<html>
    <head><title><?php echo $this->title() ?></title></head>

The title will be: My App :: Index Page

Partial Views

simple-mvc support partials. You can append partial views at runtime using partial() view helper.

<!-- index/index.phtml -->
<div class="complex">
    <?php 
      // view path, data
       echo $this->partial(
            "/path/to/complex.phtml", 
            array('title' => $this->title)
        );
    ?>
</div>

In the partial view:

<!-- path/to/complex.phtml -->
<div>
    <h3><?php echo $this->title; ?></h3>
    <!-- continue -->
</div>

Pre defined hooks (events)

simple-mvc has a list of pre defined hooks:

  1. loop.startup - called once, when the system start up
  2. pre.dispatch - called before any action
  3. post.dispatch - called after any action
  4. loop.shutdown - called once, when the system close the loop

Adding events is a simple operation, see an example with pre.dispatch hook.

<?php
$app->getEventManager()->subscribe("pre.dispatch", function($router, $app) {
    // Use a real and better auth system
    if ($_SESSION["auth"] !== true 
         && $router->getControllerName() != 'admin' 
         && $router->getActionName() != 'login') {

        // Rewrite the endpoint
        $router->setControllerName("admin");
        $router->setActionName("login");

        // Change the layout
        $app->getBootstrap("layout")->setScriptName("admin.phtml");
    }
});

Add new events

Create new hook definition:

<?php
$app->getEventManager()->subscribe("my.hook", function($app) {/*The body*/});

Raise the event:

<?php
// Call the hook named "my.hook" and pass the app as first arg.
$app->getEventManager()->publish("my.hook", array($app));

Pull driven requests

Typically MVC frameworks are "push" based. In otherwords use mechanisms to "push" data to a view and not vice-versa. A "pull" framework instead request ("pull") data from a view.

Pull strategy is useful for example during a for statement (not only for that [obviously]...). Look for an example:

<?php foreach ($this->users as $user) : ?>
<?php
    // Pull data from a controller. 
    $userDetail = $this->pull("/detail/user/id/{$user->id}");
?>
<div class="element">
    <div class="name"><?php echo $userDetail->username;?></div>
    <!-- other -->
</div>
<?php endforeach; ?>

The view require a pull operation from a controller named detail and action user. See it:

<?php
class DetailController extends Controller
{
    public function userAction()
    {
        $params = $this->getParams();

        $model = new MyModel();
        $data = $model->getById($params["id"]);

        return $data;
    }
}

You can use a "pull" controller as a normal controller with the attached view, but remember that when you request for a "pull" operation the view is never considered and the framework remove it without consider the output, only the return statement will be used.

Multiple view scripts paths

simple-mvc support multiple views scripts paths. In other words you can specify a single mount point /path/to/views after that you can add anther views script path, this mean that the simple-mvc search for a view previously into the second views path and if it is missing looks for that into the first paths. View paths are threated as a stack, the latest pushed is the first used.

During your bootstrap add more view paths

$app->bootstrap('view', function(){
    $view = new View();
    $view->addViewPath(__DIR__ . '/../views');
    $view->addViewPath(__DIR__ . '/../views-rewrite');

    return $view;
});

If you have a view named name.phtml into views folder and now you create the view named name.phtml into views-rewrite this one is used instead the original file in views folder.

Authors and Contributors

This project is maintained by @wdalmut.

Support or Contact

Having trouble with simple-mvc? You can contact me walter.dalmut@gmail.com.