then type the following command to install dependencies., (*4)
./composer.phar install
Project creation
In Tiimber, you need to create an Application class where you put your entry point and for create a Tiimber App, you need to use Tiimber ApplicationTrait., (*5)
create Blog/Application.php, (*6)
<?php
namespace Blog;
use Tiimber\Traits\{ApplicationTrait as Tiimber, ServerTrait as Server};
class Application
{
use Tiimber, Server;
private function prepare()
{
$this->setRoot(dirname(__DIR__));
$this->setCacheFolder(dirname(__DIR__) . '/cache');
$this->setHost('localhost');
$this->setPort(1337);
}
public function start()
{
$this->prepare();
$this->chop();
$this->runHttpServer($this->runApp());
}
}
Then we need to create a index.php and call your Application., (*7)
create index.php, (*8)
<?php
require __DIR__ . '/vendor/autoload.php';
(new Blog\Application())->start();
Hello world
To create an hello world we need 3 components a route, a layout and a view., (*9)
Route creation :
create config/routes.json, (*10)
{
"hello": {
"route": "/hello"
}
}
Layout creation :
A minimal Layout is composed by one constant., (*11)
The const TPL is your template. A Layout template expose outlets and the way to declare an outlet is by encapsulating it into {{{ }}}., (*12)
In the following Layout we expose only one outlet content., (*13)
create Blog\Layouts\DefaultLayout.php, (*14)
<?php
namespace Blog\Layouts;
use Tiimber\Layout;
class DefaultLayout extends Layout
{
const TPL = '{{{content}}}';
}
View creation :
A minimal view it's composed of two constants., (*15)
The EVENTS constant is array who the key represent the event to listen and outlet location., (*16)
The TPL constant is your view template., (*17)
To create the HelloWorldView, we need to print Hello world into content outlet declared into the DefaultLayout when a request is received in hello route defined into config/routes.json file., (*18)
And try it in your navigator by calling the URL http://localhost:1337/hello ., (*22)
Templating a Layout
Currently our layout is pretty simple then we need to upgrade it to create more outlets. We want to add a navigation and a footer to our Layout., (*23)
<?php
namespace Blog\Views;
use Tiimber\View;
class FooterView extends View
{
const EVENTS = [
'request::hello' => 'footer'
];
const TPL = 'Power by Tiimber';
}
Now you can test it :
Restart the php server, (*28)
php index.php
If you go to http://localhost:1337/hello there is no problem and evrething work fine but if you click on the link My Blog you have a empty page., (*29)
Then we need to upgrade our NaviagtionView and FooterView to catch more globally the request event. For that we need to use wildcard represented by *., (*30)
<?php
namespace Blog\Views\Errors;
use Tiimber\View;
use Tiimber\Http\{Request, Response};
class ServerErrorView extends View
{
const EVENTS = [
'error::500' => 'content'
];
const TPL = <<<EOF
<div>{{message}}</div>
{{stack}}
EOF;
private $error;
public function onCall(Request $req, Response $res)
{
$this->error = $req->getArgs()->get('error');
}
public function render(): array
{
return [
'message' => $this->error->getMessage(),
'stack' => $this->error->getTraceAsString()
];
}
}
Use logger
You have implemented some views and some errors, but certainly want see what happen in Tiimber. For that you need to use a logger. Currently there is two types of logger in Tiimber : FileLogger and SysLogger. In our case we gonna use SysLogger., (*44)
in Blog\Application.php, (*45)
<?php
// ...
use Tiimber\Loggers\SysLogger as Logger;
class Application
{
private function prepare()
{
// ...
(new Logger());
}
// ...
}
Thats all., (*46)
Now if you restart your server, you certainly show lot of line when you call a page from your navigator., (*47)
You can implement your own logger if you want, for that, just take a quick look of FileLogger or SysLogger., (*48)
Dynamize your views
Your tiimber view can be more dynamic, the goal of this tuto is to create a simple blog, then we need to the capacity to create and read posts., (*49)
Tiimber don't have ORM it let you the choice and the implementation., (*50)
For this tuto we use a really simple ORM called RedBeanPHP., (*51)
Our article route need to be dynamic for that pass dynamical params by defining beetwen { } and in the section params you can add the regex to validate the params., (*61)
create Blog/Views/Articles/ShowView.php, (*62)
<?php
namespace Blog\Views\Articles;
use Tiimber\View;
use Tiimber\Http\{Request, Response};
use RedBeanPHP\R;
class ShowView extends View
{
const EVENTS = [
'request::article::show' => 'content'
];
const TPL = <<<HTML
<h2>{{article.title}}</h2>
{{article.content}}, (*63)
HTML;
private $article;
public function onGet(Request $req, Response $res)
{
$this->article = R::load('article', (integer)$req->getArgs()->get('id'));
}
public function render(): array
{
return ['article' => $this->article];
}
}
In this view we use a new method onGet who received two parameters the request and the url arguments. The args variable is an array and contain the id defined into the route article::show., (*64)
With that id we can load our article., (*65)
But we stilln't have article in our database then we need a way to contribute it, (*66)
Views and Actions
Form creation :
The first stape of this part is create a form., (*67)
Then for that we need a new route and view., (*68)
An Action in tiimber is work like a View with the difference they have nothing to display. Then they dont have to specify an outlet where to be displayed or TPL constant or a render method., (*78)
In this case we go to use an Action to save our article., (*79)
<?php
namespace Blog\Actions\Articles;
use Tiimber\Action;
use Tiimber\Http\{Request, Response};
use RedBeanPHP\R;
class SaveAction extends Action
{
use RedirectTrait;
const EVENTS = [
'request::article::manage'
];
private function prepare($id)
{
if ($id !== 'new') {
return R::load('article', $id);
} else {
return R::dispense('article');
}
}
public function onPost(Request $req, Response $res)
{
$article = $this->prepare($req->getArgs()->get('id'));
$article->title = $req->getPost->get('title');
$article->content = $req->getPost->get('content');
$id = R::store($this->article);
$res->redirect('/'.$id);
}
}
In this action create a create or update an article then when the save is done we redirect to the article. To redirect you need to use the redirect trait becaue if the traditionnal headers() is not enough and you can't stop the server., (*81)
You can see we use the method onPost. By this way we don't pass into the onGet on the view. Is really important to specify method="post" in your form to access at the goods methods into your view an action., (*82)
Tips: a view and a action have access to the methods onGet and onPost;, (*83)
Sub rendering
To finish our app we go to create a quick identification based on php sessions. And use render event to add new section., (*84)
in Blog\Views\NavigationView.php, (*85)
<?php
// ...
use Tiimber\View;
use Tiimber\Http\{Request, Response};
class NavigationView extends View
{
// ...
const TPL = <<<HTML
<ul>
<li><a href="/">
My Blog
</a></li>
<li><a href="/hello">Hello</a></li>
{{#user}}
<li><a href="/article/new">New article</a></li>
{{/user}}
<li>{{{login}}}</li>
</ul>
HTML;
$this->logged= false;
public function onCall(Request $req, Response $res)
{
$this->logged = $req->getSession()->has('user');
}
public function render(): array
{
return [
'user' => $this->logged
];
}
}
In Navigation we create an outlet called login and we check if there is a user in session to add New article link., (*86)
Then we go to create a view who was rendered into login outlet., (*87)
Create LoginView
create Blog\Views\User\LoginView.php, (*88)
<?php
namespace Blog\Views\User;
use Tiimber\View;
use Tiimber\Http\{Request, Response};
class LoginView extends View
{
const EVENTS = [
'render::navigation' => 'login'
];
const TPL = <<<HTML
{{#user}}
<b>Hello {{user}}!</b>
{{/user}}
{{^user}}
{{/user}}
HTML;
private $user;
public function onCall(Request $res, Response $res)
{
$this->user = $res->getSession()->get('user');
}
public function render(): array
{
return [
'user' => $this->user
];
}
}
This view listen the event render::navigation, (*89)
Create LoginAction
Like the article, we go to create an action to save our user in session., (*90)