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:
-
loop.startup
- called once, when the system start up -
pre.dispatch
- called before anyaction
-
post.dispatch
- called after anyaction
-
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.