In symfony, a view consists of two distinct parts:
- The HTML presentation of the action result (stored in the template, in the layout, and in the template fragments)
- All the rest, including the following:
- Meta declarations: Keywords, description, or cache duration.
- Page title: Not only does it help users with several browser windows open to find yours, but it is also very important for search sites' indexing.
- File inclusions: JavaScript and style sheet files.
- Layout: Some actions require a custom layout (pop-ups, ads, and so on) or no layout at all (such as Ajax actions).
In the view, all that is not HTML is called view configuration, and symfony provides two ways to manipulate it. The usual way is through the view.yml
configuration file. It can be used whenever the values don't depend on the context or on database queries. When you need to set dynamic values, the alternative method is to set the view configuration via the sfResponse
object attributes directly in the action.
Note If you ever set a view configuration parameter both via the sfResponse
object and via the view.yml
file, the sfResponse
definition takes precedence.
7.3.1. The view.yml File
Each module can have one view.yml
file defining the settings of its views. This allows you to define view settings for a whole module and per view in a single file. The first-level keys of the view.yml
file are the module view names. Listing 7-16 shows an example of view configuration.
Listing 7-16 - Sample Module-Level view.yml
editSuccess:
metas:
title: Edit your profile
editError:
metas:
title: Error in the profile edition
all:
stylesheets: [my_style]
metas:
title: My website
Caution Be aware that the main keys in the view.yml
file are view names, not action names. As a reminder, a view name is composed of an action name and an action termination. For instance, if the edit
action returns sfView::SUCCESS
(or returns nothing at all, since it is the default action termination), then the view name is editSuccess
.
The default settings for the module are defined under the all:
key in the module view.yml
. The default settings for all the application views are defined in the application view.yml
. Once again, you recognize the configuration cascade principle:
- In
apps/myapp/modules/mymodule/config/view.yml
, the per-view definitions apply only to one view and override the module-level definitions. - In
apps/myapp/modules/mymodule/config/view.yml
, theall:
definitions apply to all the actions of the module and override the application-level definitions. - In
apps/myapp/config/view.yml
, thedefault:
definitions apply to all modules and all actions of the application.
Tip Module-level view.yml
files don't exist by default. The first time you need to adjust a view configuration parameter for a module, you will have to create an empty view.yml
in its config/
directory.
After seeing the default template in Listing 7-5 and an example of a final response in Listing 7-6, you may wonder where the header values come from. As a matter of fact, they are the default view settings, defined in the application view.yml
and shown in Listing 7-17.
Listing 7-17 - Default Application-Level View Configuration, in apps/myapp/config/view.yml
default:
http_metas:
content-type: text/html
metas:
title: symfony project
robots: index, follow
description: symfony project
keywords: symfony, project
language: en
stylesheets: [main]
javascripts: [ ]
has_layout: on
layout: layout
Each of these settings will be described in detail in the "View Configuration Settings" section.
7.3.2. The Response Object
Although part of the view layer, the response object is often modified by the action. Actions can access the symfony response object, called sfResponse
, via the getResponse()
method. Listing 7-18 lists some of the sfResponse
methods often used from within an action.
Listing 7-18 - Actions Have Access to the sfResponse
Object Methods
class mymoduleActions extends sfActions
{
public function executeIndex()
{
$response = $this->getResponse();
// HTTP headers
$response->setContentType('text/xml');
$response->setHttpHeader('Content-Language', 'en');
$response->setStatusCode(403);
$response->addVaryHttpHeader('Accept-Language');
$response->addCacheControlHttpHeader('no-cache');
// Cookies
$response->setCookie($name, $content, $expire, $path, $domain);
// Metas and page headers
$response->addMeta('robots', 'NONE');
$response->addMeta('keywords', 'foo bar');
$response->setTitle('My FooBar Page');
$response->addStyleSheet('custom_style');
$response->addJavaScript('custom_behavior');
}
}
In addition to the setter methods shown here, the sfResponse
class has getters that return the current value of the response attributes.
The header setters are very powerful in symfony. Headers are sent as late as possible (in the sfRenderingFilter
), so you can alter them as much as you want and as late as you want. They also provide very useful shortcuts. For instance, if you don't specify a charset when you call setContentType()
, symfony automatically adds the default charset defined in the settings.yml
file.
$response->setContentType('text/xml');
echo $response->getContentType();
=> 'text/xml; charset=utf-8'
The status code of responses in symfony is compliant with the HTTP specification. Exceptions return a status 500, pages not found return a status 404, normal pages return a status 200, pages not modified can be reduced to a simple header with status code 304 (see Chapter 12 for details), and so on. But you can override these defaults by setting your own status code in the action with the setStatusCode()
response method. You can specify a custom code and a custom message, or simply a custom code--in which case, symfony will add the most common message for this code.
$response->setStatusCode(404, 'This page does not exist');
Tip Before sending the headers, symfony normalizes their names. So you don't need to bother about writing content-language
instead of Content-Language
in a call to setHttpHeader()
, as symfony will understand the former and automatically transform it to the latter.
7.3.3. View Configuration Settings
You may have noticed that there are two kinds of view configuration settings:
- The ones that have a unique value (the value is a string in the
view.yml
file and the response uses aset
method for those) - The ones with multiple values (for which
view.yml
uses arrays and the response uses anadd
method)
Keep in mind that the configuration cascade erases the unique value settings but piles up the multiple values settings. This will become more apparent as you progress through this chapter.
7.3.3.1. Meta Tag Configuration
The information written in the <meta>
tags in the response is not displayed in a browser but is useful for robots and search engines. It also controls the cache settings of every page. Define these tags under the http_metas:
and metas:
keys in view.yml
, as in Listing 7-19, or with the addHttpMeta()
and addMeta()
response methods in the action, as in Listing 7-20.
Listing 7-19 - Meta Definition As Key: Value Pairs in view.yml
http_metas:
cache-control: public
metas:
description: Finance in France
keywords: finance, France
Listing 7-20 - Meta Definition As Response Settings in the Action
$this->getResponse()->addHttpMeta('cache-control', 'public');
$this->getResponse()->addMeta('description', 'Finance in France');
$this->getResponse()->addMeta('keywords', 'finance, France');
Adding an existing key will replace its current content by default. For HTTP meta tags, you can add a third parameter and set it to false
to have the addHttpMeta()
method (as well as the setHttpHeader()
) append the value to the existing one, rather than replacing it.
$this->getResponse()->addHttpMeta('accept-language', 'en');
$this->getResponse()->addHttpMeta('accept-language', 'fr', false);
echo $this->getResponse()->getHttpHeader('accept-language');
=> 'en, fr'
In order to have these meta tags appear in the final document, the include_http_metas()
and include_metas()
helpers must be called in the <head>
section (this is the case in the default layout; see Listing 7-5). Symfony automatically aggregates the settings from all the view.yml
files (including the default one shown in Listing 7-17) and the response attribute to output proper <meta>
tags. The example in Listing 7-19 ends up as shown in Listing 7-21.
Listing 7-21 - Meta Tags Output in the Final Page
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="cache-control" content="public" />
<meta name="robots" content="index, follow" />
<meta name="description" content="Finance in France" />
<meta name="keywords" content="finance, France" />
As a bonus, the HTTP header of the response is also impacted by the http-metas:
definition, even if you don't have any include_http_metas()
helpers in the layout, or if you have no layout at all. For instance, if you need to send a page as plain text, define the following view.yml
:
http_metas:
content-type: text/plain
has_layout: false
7.3.3.2. Title Configuration
The page title is a key part to search engine indexing. It is also very useful with modern browsers that provide tabbed browsing. In HTML, the title is both a tag and meta information of the page, so the view.yml
file sees the title:
key as a child of the metas:
key. Listing 7-22 shows the title definition in view.yml
, and Listing 7-23 shows the definition in the action.
Listing 7-22 - Title Definition in view.yml
indexSuccess:
metas:
title: Three little piggies
Listing 7-23 - Title Definition in the Action--Allows for Dynamic Titles
$this->getResponse()->setTitle(sprintf('%d little piggies', $number));
In the <head>
section of the final document, the title definition sets the <meta name="title">
tag if the include_metas()
helper is present, and the <title>
tag if the include_title()
helper is present. If both are included (as in the default layout of Listing 7-5), the title appears twice in the document source (see Listing 7-6), which is harmless.
7.3.3.3. File Inclusion Configuration
Adding a specific style sheet or JavaScript file to a view is easy, as Listings 7-24 and 7-25 demonstrate.
Listing 7-24 - File Inclusion in view.yml
indexSuccess:
stylesheets: [mystyle1, mystyle2]
javascripts: [myscript]
Listing 7-25 - File Inclusion in the Action
$this->getResponse()->addStylesheet('mystyle1');
$this->getResponse()->addStylesheet('mystyle2');
$this->getResponse()->addJavascript('myscript');
In each case, the argument is a file name. If the file has a logical extension (.css
for a style sheet and .js
for a JavaScript file), you can omit it. If the file has a logical location (/css/
for a style sheet and /js/
for a JavaScript file), you can omit it as well. Symfony is smart enough to figure out the correct extension or location.
Unlike the meta and title definitions, the file inclusion definitions don't require any helper in the template or layout to be included. This means that the previous settings will output the HTML code of Listing 7-26, whatever the content of the template and the layout.
Listing 7-26 - File Inclusion Result--No Need for a Helper Call in the Layout
<head>
...
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle1.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle2.css" />
<script language="javascript" type="text/javascript" src="/js/myscript.js">
</script>
</head>
Note Style sheet and JavaScript inclusions in the response are performed by a filter called sfCommonFilter
. It looks for a <head>
tag in the response, and adds the <link>
and <script>
just before the closing </head>
. This means that the inclusion can't take place if there is no <head>
tag in your layout or templates.
Remember that the configuration cascade principle applies, so any file inclusion defined in the application view.yml
makes it appear in every page of the application. Listings 7-27, 7-28, and 7-29 demonstrate this principle.
Listing 7-27 - Sample Application view.yml
default:
stylesheets: [main]
Listing 7-28 - Sample Module view.yml
indexSuccess:
stylesheets: [special]
all:
stylesheets: [additional]
Listing 7-29 - Resulting indexSuccess
View
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" />
If you need to remove a file defined at a higher level, just add a minus sign (-
) in front of the file name in the lower-level definition, as shown in Listing 7-30.
Listing 7-30 - Sample Module view.yml
That Removes the Files Defined at the Application Level
indexSuccess:
stylesheets: [-main, special]
all:
stylesheets: [additional]
To remove all style sheets or JavaScript files, use the following syntax:
indexSuccess:
stylesheets: [-*]
javascripts: [-*]
You can be more accurate and define an additional parameter to force the position where to include the file (first or last position):
// In the view.yml
indexSuccess:
stylesheets: [special: { position: first }]
// In the action
$this->getResponse()->addStylesheet('special', 'first');
To specify media for a style sheet inclusion, you can change the default style sheet tag options, as shown in Listings 7-31, 7-32, and 7-33.
Listing 7-31 - Style Sheet Inclusion with Media in view.yml
indexSuccess:
stylesheets: [main, paper: { media: print }]
Listing 7-32 - Style Sheet Inclusion with Media in the Action
$this->getResponse()->addStylesheet('paper', '', array('media' => 'print'));
Listing 7-33 - Resulting View
<link rel="stylesheet" type="text/css" media="print" href="/css/paper.css" />
7.3.3.4. Layout Configuration
According to the graphical charter of your website, you may have several layouts. Classic websites have at least two: the default layout and the pop-up layout.
You have already seen that the default layout is myproject/apps/myapp/templates/layout.php
. Additional layouts must be added in the same global templates/
directory. If you want a view to use a myapp/templates/my_layout.php
file, use the syntax shown in Listing 7-34 in view.yml
or in Listing 7-35 in the action.
Listing 7-34 - Layout Definition in view.yml
indexSuccess:
layout: my_layout
Listing 7-35 - Layout Definition in the Action
$this->setLayout('my_layout');
Some views don't need any layout at all (for instance, plain text pages or RSS feeds). In that case, set has_layout
to false
, as shown in Listings 7-36 and 7-37.
Listing 7-36 - Layout Removal in view.yml
indexSuccess:
has_layout: false
Listing 7-37 - Layout Removal in the Action
$this->setLayout(false);
Note Ajax actions views have no layout by default.