Getting started with Simpletools\Mvc
Simpletools\Mvc has been designed to provide you with all benefits of the Model-View-Controller software architectural pattern. Simpletools\Mvc provides scalability, performance and ease of use out of the box.
Requirements
Simpletools framework requires PHP version >= 5.3.0
Setup
Simpletools\Mvc is very easy to setup, all you need is a composer and organise your application files and folders in the normalised way (you can also download all framework directly). Below we have demonstrated all you need to get your first app up and running using Simpletools\Mvc.
And to simplify the setup you can just run:
composer create-project simpletools/helloworld --prefer-dist
Helloworld example can be run stratight away with PHP Built-in web server server as it include a file which emulates mod_rewrite. To start just run:
php -S localhost:8000 server.php
This will start web server listening on port 8000 and localhost domain.
*Keep in mind that PHP web server was designed to aid application development. It may also be useful for testing purposes or for application demonstrations that are run in controlled environments. It is not intended to be a full-featured web server. It should not be used on a public network.
Directory structure
Simpletools\Mvc requires a structural approach to your application directory organisation therefore making it easy to understand, document and maintain.
In order to get your application to work with Simpletools\Mvc the following initial directory structure is required:
app/
application/
controllers/
ErrorController.php
IndexController.php
models/
views/
Index/
index.phtml
Error/
error.phtml
public/
bootstrap.php
vendor/
simpletools/
framework/
...
autoload.php
composer.json
Bootstrapping
After creating the initial application structure as described above we can move on to create a body of our bootstrap.php script being responsible for booting up our application. The source of that file is very simple:
<?php
require_once("../vendor/autoload.php");
$router = \Simpletools\Mvc\Router::settings(array(
"applicationDir" => "../application"
));
$router->dispatch();
?>
Index handler
To handle any requests sent by a user to our application we need to set controllers with actions and views. Controllers, actions and views will be explained further under Components section so till then lets just create an IndexController, indexAction and index view allowing us to handle any requests to our index page using the examples below.
IndexController.php:
<?php
class IndexController extends \Simpletools\Mvc\Controller
{
public function indexAction()
{
}
}
?>
index.phtml:
<html>
<body>Hello World!</body>
</html>
Errors handler
In addition to creating controllers, actions and views allowing our application to handle desired user requests it is highly recommended to handle requests which we have not intended to support in the first place, e.g. requests to not existing sections of our application. You can find more about Error handling under Components. Error controller is a very simple class which Simpletools\Mvc uses in case of requests which can"t be routed anywhere else. An example of basic Error controller, action and view we are going to use in our application can be seen below.
ErrorController.php:
<?php
class ErrorController extends \Simpletools\Mvc\Controller
{
public function errorAction()
{
}
}
?>
error.phtml:
<html>
<body>Error occured</body>
</html>
Web server configuration
And finally, to run our application we just need to create web server configuration file routing all requests to our bootstrap.php file.
For our getting started app we have chosen Apache for our web server and below you can see very basic virtual host definition file allowing our app (hosted under example.com) to run. These rules direct all requests to index.php, except when a matching file is found under the DocumentRoot.
<VirtualHost example.com:80>
ServerName example.com
DocumentRoot app/public
<Location />
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /bootstrap.php [NC,L]
</Location>
</VirtualHost>
Hello world!
After following all steps described under Setup, starting your web server and entering http://example.com in your browser you should be able to see Hello world! as per your index.phtml view.
You can also type anything else e.g. http://example.com/foo to see error.phtml being rendered.
Components
Simpletools\Mvc includes all components needed to run proper MVC application. We have listed and described their role below.
Controllers
Controllers are responsible for arbitrating the application flow. They are responsible for application"s logic - you can look at them as a proxy between your models and application views.
Each controller has a number of methods corresponding to possible actions - all suffixed with a word Action. Actions are a glue between controllers and views and can pass any data to a view.
Actions
Actions are controller"s class methods. They can request data from models, set and update them and then send required data to a view for the final processing.
Actions are a glue between controllers and views and can pass any data to a view by using the following method available for each controller:
$this->setViewProperty($key,$value);
So to pass a variable called foo with value bar into a view you can use the following code:
$this->setViewProperty("foo","bar");
$this->_view can be extened with any property which in returned can then be access by the view.
Views
Views are responsible for the presentation layer. They can access data provided to them by controller actions and present them to a user.
Below you can see an example of a controller"s action sending data to a view
$this->setViewProperty("foo","Bar");
View can display the above set variable by simply echoing $this->username property:
echo $this->foo; //will result in displaying bar.
Views can only access controller"s properties explicitly set for them inside the action under $this->_view, any other variables are not visible to views.
Models
Models are responsible for the business logic. They can interact with databases, files etc.
To define a model you just need to create a simple class as below with the name of your choice followed by word Model and saved it under app/application/models folder inside the file with the same name as the class.
In the example below we are using name Foo for our model hence after the addition of affix - Model - the file name would be FooModel.php
<?php
class FooModel
{
}
?>
Models can be accessed inside the controller"s actions by using the following command:
//just a model name - Foo, no need for affix - Model/FooModel - here
$fooModel = \Simpletools\Mvc\Model::of("Foo");
Models can have any methods you wish as well as they can be extended by any other classes.
Routing
An integral part of any MVC framework, in Simpletools\Mvc built in directly into the library so nothing to setup, all works out of box.
Routing is a process responsible for forwarding requests into the appropriate application controllers and their actions allowing to handle user requests as expected.
Routing principles
Simpletools\Mvc framework handles requests dispatching by parsing the path section of the URL in the following manner:
http://example.com/{controller}/{action}
Where {controller} and {action} section will be used to trigger the appropriate controller and its action.
So to use an example with the real values:
http://example.com/user/login
The following url results in Simpletools\Mvc invoking UserController.php and triggering ->loginAction() method of that controller, you can see source of such controller below:
<?php
class UserController extends SimpleControl
{
public function loginAction()
{
}
}
?>
After executing loginAction() by default Simpletools\Mvc moves to rendering view located inside Views/User/login.phtml and completing the routing cycle.
In case of empty values for controller or actions inside the requested url path (more about routing naming convention) Simpletools\Mvc defaults all to index so for controller it results with IndexController, and for action indexAction().
Naming convention
To prevent any confusion regarding how to name controller, action or view"s Simpletools\Mvc is normalising all before triggering each component. The rules how it works for each component have been listed below.
-
Controllers
Before deciding which controller to use Simpletools\Mvc is parsing the requested name as follow:
- lowering down all characters
- capitalising the first character
- capitalising each character followed by a non-alphanumeric character
- removing all non-alphanumeric characters
- appending word Controller at the end
-
Actions
Before deciding which action to trigger Simpletools\Mvc is parsing the requested name as follow:
- lowering down all characters
- capitalising each character followed by a non-alphanumeric character
- removing all non-alphanumeric characters
- appending word Action at the end
-
Views folder
To decide in which folder to find a requested view, Simpletools\Mvc is parsing the requested name as follow
- lowering down all characters
- capitalising the first character
- capitalising each character followed by a non-alphanumeric character
- removing all non-alphanumeric characters
-
Views
Before deciding which view to render Simpletools\Mvc is parsing the requested name as follow:
- lowering down all characters
- capitalising each character followed by a non-alphanumeric character
- removing all non-alphanumeric characters
Errors handling
Requests resulting in missing controller, action or view are being automatically re-routed to the ErrorController.php and triggers errorAction resulting in rendering error.phtml view alongside with 404 HTTP code being returned hence their importance during the initial directory setup structure described above.
Custom routing
Simpletools/Mvc also supports advanced routing allowing you to specify your own routing maps. Custom routing takes precedence over standard routing and is optional in a sense that if no custom route is found, standard routing will be used instead.
<?php
$router = \Simpletools\Mvc\Router::settings(array(
"applicationDir" => "../application",
"customRoutes" => [
"GET" => [
"/user/{userName}/settings/{id}" => "GetController@action",
],
"ANY" => [
"/user/{userName}/settings/{id}" => "AnyController@action",
]
]
));
?>
Routing namespaces
Simpletools\Mvc provides unique feature allowing you to organise your application into even more compartmental way enabling you to build apps within an app e.g /api/v1, /api/v2 having its own models, controllers, actions and views yet being able to share anything between themselves.
Setup
So to demonstrate how to setup and why to use a routing namespaces \Simpletools\Mvc provides lets assume that we need to build an api under our already existing application we have built under the Setup section. Also, to make it even more challenging lets assume that we are required to version our api in case we need to have some not backward compatible versions in the future so lets stick to /api/v1, also lets assume we need to provide foo/bar (foo - controller, bar - action) endpoint for our api so the full path looks like /api/v1/foo/bar
As you already know at that point without the routing namespaces you would endup with api as a controller and v1 as its action in which case the question arise, where and how to accommodate for all the endpoints any api must have? And that question is a reason why \Simpletools\Mvc provides the routing namespaces.
OK, so lets go to the setup so you can how easy is to solve all those challenges and how clean your application"s structure will be.
First thing we need to do is to register desired namespaces so \Simpletools\Mvc can differentiate them from normal controllers and actions.
So to enable /api/v1/foo/bar using the example.com directory structure and bootstrap we have created above we need to:
- modify our bootstrap slightly to register our /api/v1 namespace:
<?php require_once("../libs/Simpletools/Mvc.php"); $mvc = \Simpletools\Mvc::settings(array( "applicationDir" => "../application", "routingNamespaces" => array( "api/v1" ) )); $mvc->run(); ?>
- add extra directories, controllers and views into our application structure to accommodate for the /api/v1/foo/bar:
app/ application/ controllers/ Api/ V1/ FooController.php ErrorController.php IndexController.php models/ Api/ V1/ views/ Api/ V1/ Foo/ bar.phtml Index/ index.phtml Error/ error.phtml libs/ Simpletools/ Mvc.php public/ bootstrap.php
-
Create Foo controller with barAction:
<?php namespace Api/V1; class FooController extends \Simpletools\Mvc\Controller { public function barAction() { } } ?>
-
Create bar.phtml view
<html> <body>Hello World, I am /api/v1/foo/bar view!</body> </html>
And voila, job done!
Controllers
Namespacing allows controllers to communicate between each other even if they are living outside of the current namespace. You can forward requests to controllers in different namespaces or root directory e.g. to root request from our \Api\V1\FooController to \IndexController use:
$this->forward("/Index","index");
or from "\IndexController to \Api\V1\FooController
$this->forward("/Api/V1/FooController","bar");
for the convenience both characters \ and / are accepted as namespace separators.
Views
As in case of controllers namespacing allows actions to render any views, by default the view in the current namespace is being rendered but to render view from different namespace you can simply:
$this->render("\index");
for the convenience both characters \ and / are accepted as namespace separators.
Models
With namespaces models are getting extra organisation layer in form of its dedicate folders under models directory and as well as controllers can forward requests to any other places same with models, controllers can access any moder they wish including those outside of their currently active namespace. E.g. to access api/v1 models from api/v2 located controller:
\Simpletools\Mvc\Model::of("/api/v1/..");
for the convenience both characters \ and / are accepted as namespace separators.
Errors
ErrorController and view are optional in case of namespaces, in case of their non existence default one from the root directory will be used but you are free to overwrite them by creating a different one under each namespace.