logo

Tutorials: Basic MVC Guide

Routing

Framework is unique by using two completely independent MVC APIs, each supporting its own routing system.

Routing of HTTP requests

To add a route managed by STDOUT MVC API, open stdout.xml and add a tag. Example:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="user_info"/>

this route will match when relative location of requested resource is user/info. Routes can also be parameterized:

<route id="user/info/(id)" controller="Lucinda\Project\Controllers\UserInfo" view="user_info"/>

this route will match when relative location of requested resource is, for example, user/info/31254. In this case, MVC API will register a path parameter available in controllers as:

$this->attributes->getPathParameters("id")

Routing of console requests

To add a route managed by Console MVC API, open stdout.xml and add a tag using console format. Example:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="user_info" format="console"/>

this route will match when second argument of console call is user/info:

php index.php user/info 12

Routing of uncaught exceptions

To add a route managed by STDERR MVC API, open stderr.xml and add an in tag. Example:

<route id="MyException" controller="Lucinda\Project\Controllers\CustomException" view="my_exception"/>

this route will match when class name of current handled exception is MyException. Routes can also be namespaced:

<route id="\Foo\Bar\MyException" controller="Lucinda\Project\Controllers\CustomException" view="my_exception"/>

this route will match when class name of current handled exception is MyException using \Foo\Bar namespace.

Controllers

Since framework uses two completely independent MVC APIs, controllers' behavior mirrors that of routes they are defined into.

Controllers managed by STDOUT MVC API

These controllers bind http requests to responses using models. Their class name is identified from "controller" attribute in matching XML tag above and located on disk in src/Controllers folder. If, for example, route is:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="user_info"/>

then controller located will be src/Controllers/UserInfo.php. In order to be recognized as controller, class MUST extend and thus implement a public run method. Example:

namespace Lucinda\Project\Controllers; use Lucinda\Project\DAO\Users; class UserInfo extends \Lucinda\STDOUT\Controller { public function run(): void { $userID = $this->attributes->getPathParameters("id"); $users = new Users(); $this->response->view()["info"]= $users->getInfo($userID); } }

Controllers managed by Console MVC API

These controllers bind console requests to responses using models. Their class name is identified from "controller" attribute in matching XML tag above and located on disk in src/Controllers folder. If, for example, route is:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="user_info" format="console"/>

then controller located will be src/Controllers/UserInfo.php. In order to be recognized as controller, class MUST extend and thus implement a public run method. Example:

namespace Lucinda\Project\Controllers; use Lucinda\Project\DAO\Users; class UserInfo extends \Lucinda\ConsoleSTDOUT\Controller { public function run(): void { $userID = $this->request->parameters(0); $users = new Users(); $this->response->view()["info"]= $users->getInfo($userID); } }

Controllers managed by STDERR MVC API

These controllers bind uncaught exceptions to responses. Their class name is identified from "controller" attribute in matching XML tag above and located on disk in src/Controllers folder. If, for example, route is:

<route id="MyException" controller="Lucinda\Project\Controllers\CustomException" view="my_exception"/>

then controller located will be src/Controllers/CustomException.php. In order to be recognized as controller, class MUST extend and thus implement a public run method. Example:

namespace Lucinda\Project\Controllers; class CustomException extends \Lucinda\STDERR\Controller { public function run(): void { if ($this->request->getException() instanceof MyException) { $this->response->view()->setFile($this->application->getViewsPath()."/my_exception"); } else { $this->response->view()->setFile($this->application->getViewsPath()."/custom_exception"); } } }

Models

Models are PHP classes performing logic (querying databases, for example), used by controllers mainly and found in application/models folder. Framework comes with a few, but the vast majority are to be done by developers.

How to implement models that work with databases

For example, let's imagine we have a MySQL table with this structure expressing an users concept:

create table users ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, username VARCHAR(255) NOT NULL, password CHAR(20) NOT NULL, name VARCHAR(255) NOT NULL, age TINYINT UNSIGNED NOT NULL, PRIMARY KEY(id), UNIQUE(username) )

And your application requires two operations on the users concept: login and get all. In that case you should create a class in src/DAO/Entities folder that encapsulates coordinates of concept:

namespace Lucinda\Project\DAO\Entities; class User { public $id; public $name; public $age; }

Then create another in src/DAO that encapsulates operations on concept:

namespace Lucinda\Project\DAO; use Lucinda\Project\DAO\Entities\User; class Users { public function login($username, $password) { return \SQL("SELECT id FROM users WHERE username=:username AND password=:password", array( ":username"=>$username, ":password"=>sha1($password) ))->toValue(); } public function getAll() { $output = array(); $resultSet = \SQL("SELECT * FROM users"); while ($row = $resultSet->toRow()) { $user = new User(); $user->id = $row["id"]; $user->name = $row["name"]; $user->age = $row["age"]; $output[] = $user; } return $output; } }

In order for this to work, you MUST configure SQL database connection using this Tutorial!

Views

Framework by default supports html and json for both STDOUT views and STDERR views.

Creating HTML Views

By default, all HTML views are templated using View Language API. All views are located in templates/views folder as HTML files based on value of "view" attribute in matching tag. Example:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="user_info"/>

then view located will be templates/views/user_info.html. Views can also be in subfolders:

<route id="user/info" controller="Lucinda\Project\Controllers\UserInfo" view="foo/bar/user_info"/>

then view located will be templates/views/foo/bar/user_info.html.

Let us create view templates/views/user_info.html, using ViewLanguage templating:

<import file="header"/> <p>User info is:</p> <user:info name="${data.info.name}" age="${data.info.age}"> <import file="footer"/>

As one can see above, view is nothing but a combination of tags and expressions. Some tags are HTML standard (<p>), others load other views (<import>) and others are user defined (<user:info>). All user defined tags have this structure:

<libraryName:tagName attribute1="value1" ... />

where tagName reflects into a html file by same name found in libraryName folder, itself found in templates/tags folder. To create <user:info> tag we will thus need to have templates/tags/user/info.html file with a body like:

name = $[name]<br/> age = $[age]

To learn more about View Language API and how it templates HTML views, click on its link.

Creating Console Views

Views rendered via Console MVC API use same templating strategy as HTML ones, with the difference that terminal will not be able to render HTML. To make things easy in styling responses, a pseudo-HTML Console Templating Language can be used in views. Example:

<div style="background-color: blue"/>Hello, ${data.info.name}!</div>

Above will automatically render a text with blue background on UNIX-based systems and a simple text under Windows-based systems (not allowing any styling except spacing). To learn more, check language documentation!

Creating JSON Views

JSON views are handled automatically by framework based on response status and data sent by controllers to response via attributes. If response was successful, then view will look like this example:

{"status":"ok", "body":["hello":"world"]}

This assumes controller sent to response a:

$this->response->view()["hello"] = "world";

If, however, response was unsuccessful, then view will look like this example:

{"status":"error", "body":"ERROR MESSAGE"}
×