Views
Views are the V in MVC. Views are responsible for generating the specificoutput required for the request. Often this is in the form of HTML, XML, orJSON, but streaming files and creating PDF’s that users can download are alsoresponsibilities of the View Layer.
CakePHP comes with a few built-in View classes for handling the most commonrendering scenarios:
- To create XML or JSON webservices you can use the JSON and XML views.
- To serve protected files, or dynamically generated files, you can useSending Files.
- To create multiple themed views, you can use Themes.
The App View
AppView
is your application’s default View class. AppView
itself extendsthe Cake\View\View
class included in CakePHP and is defined insrc/View/AppView.php as follows:
- <?php
- namespace App\View;
- use Cake\View\View;
- class AppView extends View
- {
- }
You can use your AppView
to load helpers that will be used for every viewrendered in your application. CakePHP provides an initialize()
method thatis invoked at the end of a View’s constructor for this kind of use:
- <?php
- namespace App\View;
- use Cake\View\View;
- class AppView extends View
- {
- public function initialize(): void
- {
- // Always enable the MyUtils Helper
- $this->loadHelper('MyUtils');
- }
- }
View Templates
The view layer of CakePHP is how you speak to your users. Most of the time yourviews will be rendering HTML/XHTML documents to browsers, but you might alsoneed to reply to a remote application via JSON, or output a CSV file for a user.
CakePHP template files are regular PHP files and utilize the alternative PHP syntaxfor control structures and output. These files contain the logic necessary toprepare the data received from the controller into a presentation format that isready for your audience.
Alternative Echos
Echo, or print a variable in your template:
- <?php echo $variable; ?>
Using Short Tag support:
- <?= $variable ?>
Alternative Control Structures
Control structures, like if
, for
, foreach
, switch
, and while
can be written in a simplified format. Notice that there are no braces. Instead,the end brace for the foreach
is replaced with endforeach
. Each of thecontrol structures listed above has a similar closing syntax: endif
,endfor
, endforeach
, and endwhile
. Also notice that instead of usinga semicolon
after each structure (except the last one), there is acolon
.
The following is an example using foreach
:
- <ul>
- <?php foreach ($todo as $item): ?>
- <li><?= $item ?></li>
- <?php endforeach; ?>
- </ul>
Another example, using if/elseif/else. Notice the colons:
- <?php if ($username === 'sally'): ?>
- <h3>Hi Sally</h3>
- <?php elseif ($username === 'joe'): ?>
- <h3>Hi Joe</h3>
- <?php else: ?>
- <h3>Hi unknown user</h3>
- <?php endif; ?>
If you’d prefer using a templating language likeTwig, a subclass of View will bridge yourtemplating language and CakePHP.
Template files are stored in templates/, in a folder named after thecontroller that uses the files, and named after the action it corresponds to.For example, the view file for the Products
controller’s view()
action, wouldnormally be found in templates/Products/view.php.
The view layer in CakePHP can be made up of a number of different parts. Eachpart has different uses, and will be covered in this chapter:
- templates: Templates are the part of the page that is unique to the actionbeing run. They form the meat of your application’s response.
- elements: small, reusable bits of view code. Elements are usually renderedinside views.
- layouts: template files that contain presentational code that wraps manyinterfaces in your application. Most views are rendered inside a layout.
- helpers: these classes encapsulate view logic that is needed in manyplaces in the view layer. Among other things, helpers in CakePHP can help youbuild forms, build AJAX functionality, paginate model data, or serve RSSfeeds.
- cells: these classes provide miniature controller-like features forcreating self contained UI components. See the View Cellsdocumentation for more information.
View Variables
Any variables you set in your controller with set()
will be available inboth the view and the layout your action renders. In addition, any set variableswill also be available in any element. If you need to pass additional variablesfrom the view to the layout you can either call set()
in the view template,or use View Blocks.
You should remember to always escape any user data before outputting it asCakePHP does not automatically escape output. You can escape user content withthe h()
function:
- <?= h($user->bio); ?>
Setting View Variables
Views have a set()
method that is analogous to the set()
found inController objects. Using set() from your view file will add the variables tothe layout and elements that will be rendered later. SeeSetting View Variables for more information on using set()
.
In your view file you can do:
- $this->set('activeMenuButton', 'posts');
Then, in your layout, the $activeMenuButton
variable will be available andcontain the value ‘posts’.
Extending Views
View extending allows you to wrap one view in another. Combining this withview blocks gives you a powerful way to keep your viewsDRY. For example, your application has a sidebar that needs to changedepending on the specific view being rendered. By extending a common view file,you can avoid repeating the common markup for your sidebar, and only define theparts that change:
- <!-- templates/Common/view.php -->
- <h1><?= h($this->fetch('title')) ?></h1>
- <?= $this->fetch('content') ?>
- <div class="actions">
- <h3>Related actions</h3>
- <ul>
- <?= $this->fetch('sidebar') ?>
- </ul>
- </div>
The above view file could be used as a parent view. It expects that the viewextending it will define the sidebar
and title
blocks. The content
block is a special block that CakePHP creates. It will contain all theuncaptured content from the extending view. Assuming our view file has a$post
variable with the data about our post, the view could look like:
- <!-- templates/Posts/view.php -->
- <?php
- $this->extend('/Common/view');
- $this->assign('title', $post->title);
- $this->start('sidebar');
- ?>
- <li>
- <?php
- echo $this->Html->link('edit', [
- 'action' => 'edit',
- $post->id
- ]);
- ?>
- </li>
- <?php $this->end(); ?>
- // The remaining content will be available as the 'content' block
- // In the parent view.
- <?= h($post->body) ?>
The post view above shows how you can extend a view, and populate a set ofblocks. Any content not already in a defined block will be captured and put intoa special block named content
. When a view contains a call to extend()
,execution continues to the bottom of the current view file. Once it is complete,the extended view will be rendered. Calling extend()
more than once in aview file will override the parent view that will be processed next:
$this->extend('/Common/view'); $this->extend('/Common/index');
The above will result in /Common/index.php being rendered as the parent viewto the current view.
You can nest extended views as many times as necessary. Each view can extendanother view if desired. Each parent view will get the previous view’s contentas the content
block.
Note
You should avoid using content
as a block name in your application.CakePHP uses this for uncaptured content in extended views.
You can get the list of all populated blocks using the blocks()
method:
$list = $this->blocks();
Using View Blocks
View blocks provide a flexible API that allows you to define slots or blocks inyour views/layouts that will be defined elsewhere. For example, blocks are idealfor implementing things such as sidebars, or regions to load assets at thebottom/top of the layout. Blocks can be defined in two ways: either as acapturing block, or by direct assignment. The start()
, append()
,prepend()
, assign()
, fetch()
, and end()
methods allow you towork with capturing blocks:
// Create the sidebar block. $this->start('sidebar'); echo $this->element('sidebar/recent_topics'); echo $this->element('sidebar/recent_comments'); $this->end(); // Append into the sidebar later on. $this->start('sidebar'); echo $this->fetch('sidebar'); echo $this->element('sidebar/popular_topics'); $this->end();
You can also append into a block using append()
:
$this->append('sidebar'); echo $this->element('sidebar/popular_topics'); $this->end(); // The same as the above. $this->append('sidebar', $this->element('sidebar/popular_topics'));
If you need to clear or overwrite a block there are a couple of alternatives.The reset()
method will clear or overwrite a block at any time. Theassign()
method with an empty content string can also be used to clear thespecified block.:
// Clear the previous content from the sidebar block. $this->reset('sidebar'); // Assigning an empty string will also clear the sidebar block. $this->assign('sidebar', '');
Assigning a block’s content is often useful when you want to convert a viewvariable into a block. For example, you may want to use a block for the pagetitle, and sometimes assign the title as a view variable in the controller:
// In view file or layout above $this->fetch('title') $this->assign('title', $title);
The prepend()
method allows you to prepend content to an existing block:
// Prepend to sidebar $this->prepend('sidebar', 'this content goes on top of sidebar');
Displaying Blocks
You can display blocks using the fetch()
method. fetch()
will output ablock, returning ‘’ if a block does not exist:
<?= $this->fetch('sidebar') ?>
You can also use fetch to conditionally show content that should surround ablock should it exist. This is helpful in layouts, or extended views where youwant to conditionally show headings or other markup:
// In templates/layout/default.php <?php if ($this->fetch('menu')): ?> <div class="menu"> <h3>Menu options</h3> <?= $this->fetch('menu') ?> </div> <?php endif; ?>
You can also provide a default value for a block if it does not exist.This allows you to add placeholder content when a block does not exist.You can provide a default value using the second argument:
<div class="shopping-cart"> <h3>Your Cart</h3> <?= $this->fetch('cart', 'Your cart is empty') ?> </div>
Using Blocks for Script and CSS Files
The HtmlHelper
ties into view blocks, and its script()
, css()
, andmeta()
methods each update a block with the same name when used with theblock = true
option:
<?php // In your view file $this->Html->script('carousel', ['block' => true]); $this->Html->css('carousel', ['block' => true]); ?> // In your layout file. <!DOCTYPE html> <html lang="en"> <head> <title><?= h($this->fetch('title')) ?></title> <?= $this->fetch('script') ?> <?= $this->fetch('css') ?> </head> // Rest of the layout follows
The Cake\View\Helper\HtmlHelper
also allows you to control whichblock the scripts and CSS go to:
// In your view $this->Html->script('carousel', ['block' => 'scriptBottom']); // In your layout <?= $this->fetch('scriptBottom') ?>
Layouts
A layout contains presentation code that wraps around a view. Anything you wantto see in all of your views should be placed in a layout.
CakePHP’s default layout is located at templates/layout/default.php.If you want to change the overall look of your application, then this is theright place to start, because controller-rendered view code is placed inside ofthe default layout when the page is rendered.
Other layout files should be placed in templates/layout. When you createa layout, you need to tell CakePHP where to place the output of your views. Todo so, make sure your layout includes a place for $this->fetch('content')
Here’s an example of what a default layout might look like:
<!DOCTYPE html> <html lang="en"> <head> <title><?= h($this->fetch('title')) ?></title> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> <!-- Include external files and scripts here (See HTML helper for more info.) --> <?php echo $this->fetch('meta'); echo $this->fetch('css'); echo $this->fetch('script'); ?> </head> <body> <!-- If you'd like some sort of menu to show up on all of your views, include it here --> <div id="header"> <div id="menu">...</div> </div> <!-- Here's where I want my views to be displayed --> <?= $this->fetch('content') ?> <!-- Add a footer to each displayed page --> <div id="footer">...</div> </body> </html>
The script
, css
and meta
blocks contain any content defined in theviews using the built-in HTML helper. Useful for including JavaScript and CSSfiles from views.
Note
When using HtmlHelper::css()
or HtmlHelper::script()
in templatefiles, specify 'block' => true
to place the HTML source in a block withthe same name. (See API for more details on usage).
The content
block contains the contents of the rendered view.
You can set the title
block content from inside your view file:
$this->assign('title', 'View Active Users');
Empty values for the title
block will be automatically replaced witha representation of the current template path, such as 'Admin/Articles'
.
You can create as many layouts as you wish: just place them in thetemplates/layout directory, and switch between them inside of yourcontroller actions using the controller or view’s $layout
property:
// From a controller public function view() { // Set the layout. $this->viewBuilder()->setLayout('admin'); } // From a view file $this->layout = 'loggedin';
For example, if a section of my site included a smaller ad banner space, I mightcreate a new layout with the smaller advertising space and specify it as thelayout for all controllers’ actions using something like:
namespace App\Controller; class UsersController extends AppController { public function viewActive() { $this->set('title', 'View Active Users'); $this->viewBuilder()->setLayout('default_small_ad'); } public function viewImage() { $this->viewBuilder()->setLayout('image'); // Output user image } }
Besides a default layout CakePHP’s official skeleton app also has an ‘ajax’layout. The Ajax layout is handy for crafting AJAX responses - it’s an emptylayout. (Most AJAX calls only require a bit of markup in return, rather than afully-rendered interface.)
The skeleton app also has a default layout to help generate RSS.
Using Layouts from Plugins
If you want to use a layout that exists in a plugin, you can use pluginsyntax. For example, to use the contact layout from the Contacts plugin:
namespace App\Controller; class UsersController extends AppController { public function viewActive() { $this->viewBuilder()->setLayout('Contacts.contact'); } }
Elements
Many applications have small blocks of presentation code that need to berepeated from page to page, sometimes in different places in the layout. CakePHPcan help you repeat parts of your website that need to be reused. These reusableparts are called Elements. Ads, help boxes, navigational controls, extra menus,login forms, and callouts are often implemented in CakePHP as elements. Anelement is basically a mini-view that can be included in other views, inlayouts, and even within other elements. Elements can be used to make a viewmore readable, placing the rendering of repeating elements in its own file. Theycan also help you re-use content fragments in your application.
Elements live in the templates/element/ folder, and have the .phpfilename extension. They are output using the element method of the view:
echo $this->element('helpbox');
Passing Variables into an Element
You can pass data to an element through the element’s second argument:
echo $this->element('helpbox', [ "helptext" => "Oh, this text is very helpful." ]);
Inside the element file, all the passed variables are available as members ofthe parameter array (in the same way that Controller::set()
in thecontroller works with template files). In the above example, thetemplates/element/helpbox.php file can use the $helptext
variable:
// Inside templates/element/helpbox.php echo $helptext; // Outputs "Oh, this text is very helpful."
Keep in mind that in those view vars are merged with the view vars from the viewitself. So all view vars set using Controller::set()
in the controller andView::set()
in the view itself are also available inside the element.
The View::element()
method also supports options for the element.The options supported are ‘cache’ and ‘callbacks’. An example:
echo $this->element('helpbox', [ "helptext" => "This is passed to the element as $helptext", "foobar" => "This is passed to the element as $foobar", ], [ // uses the "long_view" cache configuration "cache" => "long_view", // set to true to have before/afterRender called for the element "callbacks" => true ] );
Element caching is facilitated through the Cache
class. You can configureelements to be stored in any Cache configuration you’ve set up. This gives you agreat amount of flexibility to decide where and for how long elements arestored. To cache different versions of the same element in an application,provide a unique cache key value using the following format:
$this->element('helpbox', [], [ "cache" => ['config' => 'short', 'key' => 'unique value'] ] );
If you need more logic in your element, such as dynamic data from a datasource,consider using a View Cell instead of an element. Find out more about ViewCells.
Caching Elements
You can take advantage of CakePHP view caching if you supply a cache parameter.If set to true
, it will cache the element in the ‘default’ Cacheconfiguration. Otherwise, you can set which cache configuration should be used.See Caching for more information on configuringCache
. A simple example of caching an element would be:
echo $this->element('helpbox', [], ['cache' => true]);
If you render the same element more than once in a view and have cachingenabled, be sure to set the ‘key’ parameter to a different name each time. Thiswill prevent each successive call from overwriting the previous element()
call’scached result. For example:
echo $this->element( 'helpbox', ['var' => $var], ['cache' => ['key' => 'first_use', 'config' => 'view_long']] ); echo $this->element( 'helpbox', ['var' => $differenVar], ['cache' => ['key' => 'second_use', 'config' => 'view_long']] );
The above will ensure that both element results are cached separately. If youwant all element caching to use the same cache configuration, you can avoid somerepetition by setting View::$elementCache
to the cache configuration youwant to use. CakePHP will use this configuration when none is given.
Requesting Elements from a Plugin
If you are using a plugin and wish to use elements from within the plugin, justuse the familiar plugin syntax. If the view is being rendered for aplugin controller/action, the plugin name will automatically be prefixed ontoall elements used, unless another plugin name is present.If the element doesn’t exist in the plugin, it will look in the main APPfolder:
echo $this->element('Contacts.helpbox');
If your view is a part of a plugin, you can omit the plugin name. For example,if you are in the ContactsController
of the Contacts plugin, the following:
echo $this->element('helpbox'); // and echo $this->element('Contacts.helpbox');
are equivalent and will result in the same element being rendered.
For elements inside subfolder of a plugin(e.g., plugins/Contacts/Template/element/sidebar/helpbox.php), use thefollowing:
echo $this->element('Contacts.sidebar/helpbox');
Routing prefix and Elements
If you have a Routing prefix configured, the Element path resolution can switchto a prefix location, as Layouts and action View do.Assuming you have a prefix “Admin” configured and you call:
echo $this->element('my_element');
The element first be looked for in templates/Admin/element/. If such afile does not exist, it will be looked for in the default location.
Caching Sections of Your View
Sometimes generating a section of your view output can be expensive because ofrendered View Cells or expensive helper operations. To help make yourapplication run faster CakePHP provides a way to cache view sections:
// Assuming some local variables echo $this->cache(function () use ($user, $article) { echo $this->cell('UserProfile', [$user]); echo $this->cell('ArticleFull', [$article]); }, ['key' => 'my_view_key']);
By default cached view content will go into the View::$elementCache
cacheconfig, but you can use the config
option to change this.
View Events
Like Controller, view trigger several events/callbacks that you can use toinsert logic around the rendering life-cycle:
Event List
View.beforeRender
View.beforeRenderFile
View.afterRenderFile
View.afterRender
View.beforeLayout
View.afterLayout
You can attach application event listeners tothese events or use Helper Callbacks.
Creating Your Own View Classes
You may need to create custom view classes to enable new types of data views, oradd additional custom view-rendering logic to your application. Like mostcomponents of CakePHP, view classes have a few conventions:
- View class files should be put in src/View. For example:src/View/PdfView.php
- View classes should be suffixed with
View
. For example:PdfView
. - When referencing view class names you should omit the
View
suffix. Forexample:$this->viewBuilder()->setClassName('Pdf');
.
You’ll also want to extend View
to ensure things work correctly:
// In src/View/PdfView.php namespace App\View; use Cake\View\View; class PdfView extends View { public function render($view = null, $layout = null) { // Custom logic here. } }
Replacing the render method lets you take full control over how your content isrendered.