Table of Contents |
5. View5.1 OverviewIn the MVC pattern, presentation logic is very strictly separated from application logic. Templates for presentation in the Maintainable framework are written in HTML with lightweight embedded PHP helpers. The helpers provided by the View component are nearly identical to the excellent helpers provided by Ruby on Rails in almost every way, only they have been adapted for PHP use. 5.2 From Action to View
An action and its corresponding view share instance variables. There is no need to explicitly assign variables to a view object.
class UsersController
{
public function show()
{
$this->user = User::find( $this->params['id'] );
}
}
In the above example, $this->user is available in both the controller and its view. 5.3.1 Where are theyTemplates are stored in /app/views. Each controller has it's own directory for template files based on the name of the controller (eg. UsersController => /app/views/users). There is also a /app/views/shared directory to store templates that are shared between different controllers. 5.3.2 What goes in themTemplates are typically HTML with bits of embedded PHP code. The PHP code can only be used for presentation logic. For increased readability we use PHP's Alternative Syntax for control structures. This applies to all control structures including if, else, elseif, while, for, and switch. <? if (! $this->user): ?> <div>No User</div> <? else: ?> <div><?= Welcome, $this->h( $this->user->name ) ?></div> <? endif ?> Instance variables are passed to the view from the controller automatically. If $this->user existed in the controller, it will also exist in the action. Accessing instance variables of the view that do not exist will not cause a PHP notice as they normally would. Instead, they will simply return NULL. 5.3.3 Short tags alwaysPHP includes a feature called short_open_tag that allows you to write less verbose and more readable templates: <?php if ($foo): ?> long tags (wrong) <? if ($foo): ?> short tags (right) <?php echo $this->h($foo) ?> long tags (wrong) <?= $this->h($foo) ?> short tags (right) The short tags are much nicer but are sometimes avoided for portability. In environments where the short_open_tag feature has been disabled, they can't normally be used. The framework includes a special feature that overcomes this limitation. Short tags can be used all the time, regardless of the state of short_open_tag in the PHP environment. Short tags are preferred and should always be used. 5.3.4 Easier associative arraysThe view has many helpers available that provide powerful features such as form generation. You will use these frequently. Most of these helpers have options that are specified as an associative array.
<!--> Traditional verbose array() notation -->
<? $form = $this->formFor('message', array('url' => array('action' => 'create'))) ?>
<?= $form->textField('subject', array('class' => 'text')) ?>
<?= $form->textArea('content', array('class' => 'text', 'rows' => '5')) ?>
<? $form->end() ?>
Since we use these options so often, views provide a special feature that allows associative arrays to be written as [] instead of array(). Here's the example from above but cleaned up with the [] notation:
<!-- Shorter and more readable [] notation -->
<? $form = $this->formFor('message', ['url' => ['action' => 'create']]) ?>
<?= $form->textField('subject', ['class' => 'text']) ?>
<?= $form->textArea('content', ['class' => 'text', 'rows' => '5']) ?>
<? $form->end() ?>
Using this notation for associative arrays makes the views easier to type and read. Always use this when specifying options for helpers. 5.3.4 Always escape outputIt is your responsibility to always escape your output! WRONG <?= $this->user->name ?> Right <?= $this->h( $this->user->name ) ?> The $this->h helper method is used for escaping output. Never send data to the output without escaping it, period. 5.3.1 OverviewViews separate the presentation from the controllers and models. Views are allowed to have logic, provided that the logic is only for presentation purposes. This presentation logic is small bits of PHP code embedded in the HTML. Bits of presentation logic code can be extracted into helper methods. Once extracted, a helper method can be called in the view in place of the former code block. Extracting presentation logic into helpers is a best practice and helps views clean and DRY. Helpers are simply methods of a class. The framework mixes the helpers into the view behind the scenes, and makes the appear as methods inside the view. An example of a helper class with a single highlight() helper follows:
class UsersHelper extends ApplicationHelper
{
/**
* Highlight a phrase within the given text
* @param string $text
* @param string $phrase
* @return string
*/
public function highlight($text, $phrase)
{
$escaped = $this->h($text);
$highlighter = '<strong class="highlight">$excaped</strong>';
if (empty($phrase) || empty($text)) {
return $text;
}
return preg_replace("/($phrase)/", $highlighter, $text);
}
}
<-- in the html -->
<div><?= $this->highlight($this->var, 'bob') ?><div>
...
It is OK to put HTML into helper class methods because they exist
to assist with presentation. However, it is NOT OK to put print/echo statements
within a helper class. Helper methods always return a value that is displayed
in the view like <?= $this->highlight($text) ?>.
The name of the helper class above is UsersHelper, so we know by convention that these methods will be available to the views of UsersController. 5.3.1 OrganizationAs shown above, helpers are methods that are organized into classes. A view typically has access to helpers from many sources:
The framework will instantiate helper classes automatically and then mix them together through overloading. Inside a view, helper methods from all of the sources above be called by simply using <?= $this->helperMethod() ?>. 5.3.4 Built-In HelpersMany convenient helper classes are available within the framework itself, and the list of these will grow as time goes on. These are located in the: /vendor/Mad/View/Helper/ directory and are always available all the time. The built-in helpers mirror the helpers from Ruby on Rails almost identically. 5.5.1 What/Where are they?When building our application, most of the time you'll have a common layout between different pages. The layout being the menu, header, and navigation. The framework has a way to share this code between different actions so that we only need one shared layout between similar pages. Layout templates are stored in the app/views/layouts directory. 5.5.2 How They WorkLets take a look at an example layout template:
<!-- in /app/views/layouts/myLayout.html -->
<html>
<head>
<title><?= $this->h( $this->pageTitle ) ?></title>
</head>
<body>
<?= $this->contentForLayout ?>
</body>
</html>
You'll notice the variable <?= $this->contentForLayout ?> in the template. This is a special variable that tells us where our action template will be rendered within the layout code. We can use this layout by using the controller's setLayout() method in _initialize:
...
class UsersController
{
protected function _initialize()
{
// add this layout for all actions
$this->setLayout('myLayout');
}
protected function helloWorld()
{
$this->pageTitle = "Hello From Users!";
}
}
...
You'll notice we can also set variables in our actions to be available in the layout template. Now if we were to add a template for our helloWorld action: <!-- in /app/views/users/helloWorld.html --> <h1>Hello World</h1> When the helloWorld action renders, it will first render the action template (/app/views/users/helloWorld.html). Then it will replace our $this->contentForLayout layout variable layout with this content to produce our final result:
<!-- Final Rendered HTML -->
<html>
<head>
<title>Hello From Users!</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
5.5.3 Not All Actions!There are times when you won't want every action in the controller to use our layout (especially when using AJAX). It is easy enough to disable layout for a specific action by adding $this->useLayout(false) to that method.
class UsersController
{
protected function _initialize()
{
// set this layout for all actions
$this->setLayout('myLayout');
}
public function showSpecial()
{
// don't use layout here
$this->useLayout(false);
$this->render(array('text' => '...'));
}
}
5.6.1 OverviewPartial templates are snippets of HTML code that are reusable between different actions. They make it easy to make our templates as DRY as possible. Partial templates are named with a leading prefix to differentiate them from our normal templates. An example would be: /app/views/users/_user.html. 5.6.2 Render PartialRendering a partial template within another template is done using the helper method: render(['partial' => '...']). The leading underscore and extension is omitted when referring to the partial file.
<div>
<? foreach ($this->users as $user): ?>
$this->render(['partial' => 'user']);
<? endforeach ?>
</div>
Any view instance variables available in the main template are also available in the partial template. |