PageRenderTime 6ms CodeModel.GetById 7ms app.highlight 6ms RepoModel.GetById 4ms app.codeStats 1ms

/lib/vendor/symfony/doc/07-Inside-the-View-Layer.txt

http://selfpublish.googlecode.com/
Plain Text | 1057 lines | 738 code | 319 blank | 0 comment | 0 complexity | 8c61b7aa810a45d83d92d7e896de629a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1Chapter 7 - Inside The View Layer
  2=================================
  3
  4The view is responsible for rendering the output correlated to a particular action. In symfony, the view consists of several parts, with each part designed to be easily modified by the person who usually works with it.
  5
  6* Web designers generally work on the templates (the presentation of the current action data) and on the layout (containing the code common to all pages). These are written in HTML with small embedded chunks of PHP, which are mostly calls to helpers.
  7* For reusability, developers usually package template code fragments into partials or components. They use slots and component slots to affect more than one zone of the layout. Web designers can work on these template fragments as well.
  8* Developers focus on the YAML view configuration file (setting the properties of the response and other interface elements) and on the response object. When dealing with variables in the templates, the risks of cross-site scripting must not be ignored, and a good comprehension of output escaping techniques is required to safely record user data.
  9
 10But whatever your role is, you will find useful tools to speed up the tedious job of presenting the results of the action. This chapter covers all of these tools.
 11
 12Templating
 13----------
 14
 15Listing 7-1 shows a typical symfony template. It contains some HTML code and some basic PHP code, usually calls to variables defined in the action (via `$this->name = 'foo';`) and helpers.
 16
 17Listing 7-1 - A Sample indexSuccess.php Template
 18
 19    [php]
 20    <h1>Welcome</h1>
 21    <p>Welcome back, <?php echo $name ?>!</p>
 22    <ul>What would you like to do?
 23      <li><?php echo link_to('Read the last articles', 'article/read') ?></li>
 24      <li><?php echo link_to('Start writing a new one', 'article/write') ?></li>
 25    </ul>
 26
 27As explained in Chapter 4, the alternative PHP syntax is preferable for templates to make them readable for non-PHP developers. You should keep PHP code to a minimum in templates, since these files are the ones used to design the GUI of the application, and are sometimes created and maintained by another team, specialized in presentation but not in application logic. Keeping the logic inside the action also makes it easier to have several templates for a single action, without any code duplication.
 28
 29### Helpers
 30
 31Helpers are PHP functions that return HTML code and can be used in templates. In Listing 7-1, the `link_to()` function is a helper. Sometimes, helpers are just time-savers, packaging code snippets frequently used in templates. For instance, you can easily imagine the function definition for this helper:
 32
 33    [php]
 34    <?php echo input_tag('nickname') ?>
 35     => <input type="text" name="nickname" id="nickname" value="" />
 36
 37It should look like Listing 7-2.
 38
 39Listing 7-2 - Sample Helper Definition
 40
 41    [php]
 42    function input_tag($name, $value = null)
 43    {
 44      return '<input type="text" name="'.$name.'" id="'.$name.'"value="'.$value.'" />';
 45    }
 46
 47As a matter of fact, the `input_tag()` function built into symfony is a little more complicated than that, as it accepts a third parameter to add other attributes to the `<input>` tag. You can check its complete syntax and options in the online API documentation ([http://www.symfony-project.org/api/1_2/](http://www.symfony-project.org/api/1_2/)).
 48
 49Most of the time, helpers carry intelligence and save you long and complex coding:
 50
 51    [php]
 52    <?php echo auto_link_text('Please visit our website www.example.com') ?>
 53     => Please visit our website <a href="http://www.example.com">www.example.com</a>
 54
 55Helpers facilitate the process of writing templates and produce the best possible HTML code in terms of performance and accessibility. You can always use plain HTML, but helpers are usually faster to write.
 56
 57>**TIP**
 58>You may wonder why the helpers are named according to the underscore syntax rather than the camelCase convention, used everywhere else in symfony. This is because helpers are functions, and all the core PHP functions use the underscore syntax convention.
 59
 60#### Declaring Helpers
 61
 62The symfony files containing helper definitions are not autoloaded (since they contain functions, not classes). Helpers are grouped by purpose. For instance, all the helper functions dealing with text are defined in a file called `TextHelper.php`, called the `Text` helper group. So if you need to use a helper in a template, you must load the related helper group earlier in the template by declaring it with the `use_helper()` function. Listing 7-3 shows a template using the `auto_link_text()` helper, which is part of the `Text` helper group.
 63
 64Listing 7-3 - Declaring the Use of a Helper
 65
 66    [php]
 67    // Use a specific helper group in this template
 68    <?php use_helper('Text') ?>
 69    ...
 70    <h1>Description</h1>
 71    <p><?php echo auto_link_text($description) ?></p>
 72
 73>**TIP**
 74>If you need to declare more than one helper group, add more arguments to the `use_helper()` call. For instance, to load both the `Text` and the `Javascript` helper groups in a template, call `<?php use_helper('Text', 'Javascript') ?>`.
 75
 76A few helpers are available by default in every template, without need for declaration. These are helpers of the following helper groups:
 77
 78  * `Helper`: Required for helper inclusion (the `use_helper()` function is, in fact, a helper itself)
 79  * `Tag`: Basic tag helper, used by almost every helper
 80  * `Url`: Links and URL management helpers
 81  * `Asset`: Helpers populating the HTML `<head>` section, and providing easy links to external assets (images, JavaScript, and style sheet files)
 82  * `Partial`: Helpers allowing for inclusion of template fragments
 83  * `Cache`: Manipulation of cached code fragments
 84  * `Form`: Form input helpers
 85
 86The list of the standard helpers, loaded by default for every template, is configurable in the `settings.yml` file. So if you know that you will not use the helpers of the `Cache` group, or that you will always use the ones of the Text group, modify the standard_helpers setting accordingly. This will speed up your application a bit. You cannot remove the first four helper groups in the preceding list (`Helper`, `Tag`, `Url`, and `Asset`), because they are compulsory for the templating engine to work properly. Consequently, they don't even appear in the list of standard helpers.
 87
 88>**TIP**
 89>If you ever need to use a helper outside a template, you can still load a helper group from anywhere by calling `sfProjectConfiguration::getActive()->loadHelpers($helpers)`, where `$helpers` is a helper group name or an array of helper group names. For instance, if you want to use `auto_link_text()` in an action, you need to call `sfProjectConfiguration::getActive()->loadHelpers('Text')` first.
 90
 91#### Frequently Used Helpers
 92
 93You will learn about some helpers in detail in later chapters, in relation with the feature they are helping. Listing 7-4 gives a brief list of the default helpers that are used a lot, together with the HTML code they return.
 94
 95Listing 7-4 - Common Default Helpers
 96
 97    [php]
 98    // Helper group
 99    <?php use_helper('HelperName') ?>
100    <?php use_helper('HelperName1', 'HelperName2', 'HelperName3') ?>
101
102    // Tag group
103    <?php echo tag('input', array('name' => 'foo', 'type' => 'text')) ?>
104    <?php echo tag('input', 'name=foo type=text') ?>  // Alternative options syntax
105     => <input name="foo" type="text" />
106    <?php echo content_tag('textarea', 'dummy content', 'name=foo') ?>
107     => <textarea name="foo">dummy content</textarea>
108
109    // Url group
110    <?php echo link_to('click me', 'mymodule/myaction') ?>
111    => <a href="/route/to/myaction">click me</a>  // Depends on the routing settings
112
113    // Asset group
114    <?php echo image_tag('myimage', 'alt=foo size=200x100') ?>
115     => <img src="/images/myimage.png" alt="foo" width="200" height="100"/>
116    <?php echo javascript_include_tag('myscript') ?>
117     => <script language="JavaScript" type="text/javascript" src="/js/myscript.js"></script>
118    <?php echo stylesheet_tag('style') ?>
119     => <link href="/stylesheets/style.css" media="screen" rel="stylesheet"type="text/css" />
120
121There are many other helpers in symfony, and it would take a full book to describe all of them. The best reference for helpers is the online API documentation ([http:// www.symfony-project.org/api/1_2/](http://www.symfony-project.org/api/1_2/)), where all the helpers are well documented, with their syntax, options, and examples.
122
123#### Adding Your Own Helpers
124
125Symfony ships with a lot of helpers for various purposes, but if you don't find what you need in the API documentation, you will probably want to create a new helper. This is very easy to do.
126
127Helper functions (regular PHP functions returning HTML code) should be saved in a file called `FooBarHelper.php`, where `FooBar` is the name of the helper group. Store the file in the `apps/frontend/lib/helper/` directory (or in any `helper/` directory created under one of the `lib/` folders of your project) so it can be found automatically by the `use_helper('FooBar')` helper for inclusion.
128
129>**TIP**
130>This system even allows you to override the existing symfony helpers. For instance, to redefine all the helpers of the `Text` helper group, just create a `TextHelper.php` file in your `apps/frontend/lib/helper/` directory. Whenever you call `use_helper('Text')`, symfony will use your helper group rather than its own. But be careful: as the original file is not even loaded, you must redefine all the functions of a helper group to override it; otherwise, some of the original helpers will not be available at all.
131
132### Page Layout
133
134The template shown in Listing 7-1 is not a valid XHTML document. The `DOCTYPE` definition and the `<html>` and `<body>` tags are missing. That's because they are stored somewhere else in the application, in a file called `layout.php`, which contains the page layout. This file, also called the global template, stores the HTML code that is common to all pages of the application to avoid repeating it in every template. The content of the template is integrated into the layout, or, if you change the point of view, the layout "decorates" the template. This is an application of the decorator design pattern, illustrated in Figure 7-1.
135
136>**TIP**
137>For more information about the decorator and other design patterns, see *Patterns of Enterprise Application Architecture* by Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0).
138
139Figure 7-1 - Decorating a template with a layout
140
141![Decorating a template with a layout](/images/book/F0701.png "Decorating a template with a layout")
142
143Listing 7-5 shows the default page layout, located in the application `templates/` directory.
144
145Listing 7-5 - Default Layout, in `myproject/apps/frontend/templates/layout.php`
146
147    [php]
148    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
149    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
150      <head>
151        <?php include_http_metas() ?>
152        <?php include_metas() ?>
153        <?php include_title() ?>
154        <link rel="shortcut icon" href="/favicon.ico" />
155      </head>
156      <body>
157        <?php echo $sf_content ?>
158      </body>
159    </html>
160
161The helpers called in the `<head>` section grab information from the response object and the view configuration. The `<body>` tag outputs the result of the template. With this layout, the default configuration, and the sample template in Listing 7-1, the processed view looks like Listing 7-6.
162
163Listing 7-6 - The Layout, the View Configuration, and the Template Assembled
164
165    [php]
166    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
167    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
168      <head>
169        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
170        <meta name="title" content="symfony project" />
171        <meta name="robots" content="index, follow" />
172        <meta name="description" content="symfony project" />
173        <meta name="keywords" content="symfony, project" />
174        <title>symfony project</title>
175        <link rel="stylesheet" type="text/css" href="/css/main.css" />
176        <link rel="shortcut icon" href="/favicon.ico">
177      </head>
178      <body>
179        <h1>Welcome</h1>
180        <p>Welcome back, <?php echo $name ?>!</p>
181        <ul>What would you like to do?
182          <li><?php echo link_to('Read the last articles', 'article/read') ?></li>
183          <li><?php echo link_to('Start writing a new one', 'article/write') ?></li>
184        </ul>
185      </body>
186    </html>
187
188The global template can be entirely customized for each application. Add in any HTML code you need. This layout is often used to hold the site navigation, logo, and so on. You can even have more than one layout, and decide which layout should be used for each action. Don't worry about JavaScript and style sheet inclusion for now; the "View Configuration" section later in this chapter shows how to handle that.
189
190### Template Shortcuts
191
192In templates, a few symfony variables are always available. These shortcuts give access to the most commonly needed information in templates, through the core symfony objects:
193
194  * `$sf_context`: The whole context object (`instance of sfContext`)
195  * `$sf_request`: The request object (`instance of sfRequest`)
196  * `$sf_params` : The parameters of the request object
197  * `$sf_user`   : The current user session object (`instance of sfUser`)
198
199The previous chapter detailed useful methods of the `sfRequest` and `sfUser` objects. You can actually call these methods in templates through the `$sf_request` and `$sf_user` variables. For instance, if the request includes a `total` parameter, its value is available in the template with the following:
200
201    [php]
202    // Long version
203    <?php echo $sf_request->getParameter('total') ?>
204
205    // Shorter version
206    <?php echo $sf_params->get('total') ?>
207
208    // Equivalent to the following action code
209    echo $request->getParameter('total')
210
211Code Fragments
212--------------
213
214You may often need to include some HTML or PHP code in several pages. To avoid repeating that code, the PHP `include()` statement will suffice most of the time.
215
216For instance, if many of the templates of your application need to use the same fragment of code, save it in a file called `myFragment.php` in the global template directory (`myproject/apps/frontend/templates/`) and include it in your templates as follows:
217
218    [php]
219    <?php include(sfConfig::get('sf_app_template_dir').'/myFragment.php') ?>
220
221But this is not a very clean way to package a fragment, mostly because you can have different variable names between the fragment and the various templates including it. In addition, the symfony cache system (described in Chapter 12) has no way to detect an include, so the fragment cannot be cached independently from the template. Symfony provides three alternative types of intelligent code fragments to replace `include`s:
222
223  * If the logic is lightweight, you will just want to include a template file having access to some data you pass to it. For that, you will use a partial.
224  * If the logic is heavier (for instance, if you need to access the data model and/or modify the content according to the session), you will prefer to separate the presentation from the logic. For that, you will use a component.
225  * If the fragment is meant to replace a specific part of the layout, for which default content may already exist, you will use a slot.
226
227>**Note**
228>Another code fragment type, called a component slot, is to be used when the nature of the fragment depends on the context (for instance, if the fragment needs to be different for the actions of a given module). Component slots are described later in this chapter.
229
230The inclusion of these fragments is achieved by helpers of the `Partial` group. These helpers are available from any symfony template, without initial declaration.
231
232### Partials
233
234A partial is a reusable chunk of template code. For instance, in a publication application, the template code displaying an article is used in the article detail page, and also in the list of the best articles and the list of latest articles. This code is a perfect candidate for a partial, as illustrated in Figure 7-2.
235
236Figure 7-2 - Reusing partials in templates
237
238![Reusing partials in templates](/images/book/F0702.png "Reusing partials in templates")
239
240Just like templates, partials are files located in the `templates/` directory, and they contain HTML code with embedded PHP. A partial file name always starts with an underscore (`_`), and that helps to distinguish partials from templates, since they are located in the same `templates/` folders.
241
242A template can include partials whether it is in the same module, in another module, or in the global `templates/` directory. Include a partial by using the `include_partial()` helper, and specify the module and partial name as a parameter (but omit the leading underscore and the trailing `.php`), as described in Listing 7-7.
243
244Listing 7-7 - Including a Partial in a Template of the `mymodule` Module
245
246    [php]
247    // Include the frontend/modules/mymodule/templates/_mypartial1.php partial
248    // As the template and the partial are in the same module,
249    // you can omit the module name
250    <?php include_partial('mypartial1') ?>
251
252    // Include the frontend/modules/foobar/templates/_mypartial2.php partial
253    // The module name is compulsory in that case
254    <?php include_partial('foobar/mypartial2') ?>
255
256    // Include the frontend/templates/_mypartial3.php partial
257    // It is considered as part of the 'global' module
258    <?php include_partial('global/mypartial3') ?>
259
260Partials have access to the usual symfony helpers and template shortcuts. But since partials can be called from anywhere in the application, they do not have automatic access to the variables defined in the action calling the templates that includes them, unless passed explicitly as an argument. For instance, if you want a partial to have access to a `$total` variable, the action must hand it to the template, and then the template to the helper as a second argument of the `include_partial()` call, as shown in Listings 7-8, 7-9, and 7-10.
261
262Listing 7-8 - The Action Defines a Variable, in `mymodule/actions/actions.class.php`
263
264    [php]
265    class mymoduleActions extends sfActions
266    {
267      public function executeIndex()
268      {
269        $this->total = 100;
270      }
271    }
272
273Listing 7-9 - The Template Passes the Variable to the Partial, in `mymodule/templates/indexSuccess.php`
274
275    [php]
276    <p>Hello, world!</p>
277    <?php include_partial('mypartial', array('mytotal' => $total)) ?>
278
279Listing 7-10 - The Partial Can Now Use the Variable, in `mymodule/templates/_mypartial.php`
280
281    [php]
282    <p>Total: <?php echo $mytotal ?></p>
283
284>**TIP**
285>All the helpers so far were called by `<?php echo functionName() ?>`. The partial helper, however, is simply called by `<?php include_partial() ?>`, without `echo`, to make it behave similar to the regular PHP `include()` statement. If you ever need a function that returns the content of a partial without actually displaying it, use `get_partial()` instead. All the `include_` helpers described in this chapter have a `get_` counterpart that can be called together with an `echo` statement.
286
287>**TIP**
288>**New in symfony 1.1**: Instead of resulting in a template, an action can return a partial or a component. The `renderPartial()` and `renderComponent()` methods of the action class promote reusability of code. Besides, they take advantage of the caching abilities of the partials (see Chapter 12). The variables defined in the action will be automatically passed to the partial/component, unless you define an associative array of variables as a second parameter of the method.
289>
290>     [php]
291>     public function executeFoo()
292>     {
293>       // do things
294>       $this->foo = 1234;
295>       $this->bar = 4567;
296>
297>       return $this->renderPartial('mymodule/mypartial');
298>     }
299>
300>In this example, the partial will have access to `$foo` and `$bar`. If the action ends with the following line:
301>
302>       return $this->renderPartial('mymodule/mypartial', array('foo' => $this->foo));
303>
304>Then the partial will only have access to `$foo`.
305
306### Components
307
308In Chapter 2, the first sample script was split into two parts to separate the logic from the presentation. Just like the MVC pattern applies to actions and templates, you may need to split a partial into a logic part and a presentation part. In such a case, you should use a component.
309
310A component is like an action, except it's much faster. The logic of a component is kept in a class inheriting from `sfComponents`, located in an `actions/components.class.php` file. Its presentation is kept in a partial. Methods of the `sfComponents` class start with the word `execute`, just like actions, and they can pass variables to their presentation counterpart in the same way that actions can pass variables. Partials that serve as presentation for components are named by the component (without the leading `execute`, but with an underscore instead). Table 7-1 compares the naming conventions for actions and components.
311
312Table 7-1 - Action and Component Naming Conventions
313
314Convention               |  Actions              |  Components
315------------------------ | --------------------- | ----------------------
316Logic file               | `actions.class.php`   | `components.class.php`
317Logic class extends      | `sfActions`           | `sfComponents`
318Method naming            | `executeMyAction()`   | `executeMyComponent()`
319Presentation file naming | `myActionSuccess.php` | `_myComponent.php`
320
321>**TIP**
322>Just as you can separate actions files, the `sfComponents` class has an `sfComponent` counterpart that allows for single component files with the same type of syntax.
323
324For instance, suppose you have a sidebar displaying the latest news headlines for a given subject, depending on the user's profile, which is reused in several pages. The queries necessary to get the news headlines are too complex to appear in a simple partial, so they need to be moved to an action-like file--a component. Figure 7-3 illustrates this example.
325
326For this example, shown in Listings 7-11 and 7-12, the component will be kept in its own module (called `news`), but you can mix components and actions in a single module if it makes sense from a functional point of view.
327
328Figure 7-3 - Using components in templates
329
330![Using components in templates](/images/book/F0703.png "Using components in templates")
331
332Listing 7-11 - The Components Class, in `modules/news/actions/components.class.php`
333
334    [php]
335    <?php
336
337    class newsComponents extends sfComponents
338    {
339      public function executeHeadlines()
340      {
341        $c = new Criteria();
342        $c->addDescendingOrderByColumn(NewsPeer::PUBLISHED_AT);
343        $c->setLimit(5);
344        $this->news = NewsPeer::doSelect($c);
345      }
346    }
347
348Listing 7-12 - The Partial, in `modules/news/templates/_headlines.php`
349
350    [php]
351    <div>
352      <h1>Latest news</h1>
353      <ul>
354      <?php foreach($news as $headline): ?>
355        <li>
356          <?php echo $headline->getPublishedAt() ?>
357          <?php echo link_to($headline->getTitle(),'news/show?id='.$headline->getId()) ?>
358        </li>
359      <?php endforeach ?>
360      </ul>
361    </div>
362
363Now, every time you need the component in a template, just call this:
364
365    [php]
366    <?php include_component('news', 'headlines') ?>
367
368Just like the partials, components accept additional parameters in the shape of an associative array. The parameters are available to the partial under their name, and in the component via the `$this` object. See Listing 7-13 for an example.
369
370Listing 7-13 - Passing Parameters to a Component and Its Template
371
372    [php]
373    // Call to the component
374    <?php include_component('news', 'headlines', array('foo' => 'bar')) ?>
375
376    // In the component itself
377    echo $this->foo;
378     => 'bar'
379
380    // In the _headlines.php partial
381    echo $foo;
382     => 'bar'
383
384You can include components in components, or in the global layout, as in any regular template. Like actions, components' `execute` methods can pass variables to the related partial and have access to the same shortcuts. But the similarities stop there. A component doesn't handle security or validation, cannot be called from the Internet (only from the application itself), and doesn't have various return possibilities. That's why a component is faster to execute than an action.
385
386### Slots
387
388Partials and components are great for reusability. But in many cases, code fragments are required to fill a layout with more than one dynamic zone. For instance, suppose that you want to add some custom tags in the `<head>` section of the layout, depending on the content of the action. Or, suppose that the layout has one major dynamic zone, which is filled by the result of the action, plus a lot of other smaller ones, which have a default content defined in the layout but can be overridden at the template level.
389
390For these situations, the solution is a slot. Basically, a slot is a placeholder that you can put in any of the view elements (in the layout, a template, or a partial). Filling this placeholder is just like setting a variable. The filling code is stored globally in the response, so you can define it anywhere (in the layout, a template, or a partial). Just make sure to define a slot before including it, and remember that the layout is executed after the template (this is the decoration process), and the partials are executed when they are called in a template. Does it sound too abstract? Let's see an example.
391
392Imagine a layout with one zone for the template and two slots: one for the sidebar and the other for the footer. The slot values are defined in the templates. During the decoration process, the layout code wraps the template code, and the slots are filled with the previously defined values, as illustrated in Figure 7-4. The sidebar and the footer can then be contextual to the main action. This is like having a layout with more than one "hole".
393
394Figure 7-4 - Layout slots defined in a template
395
396![Layout slots defined in a template](/images/book/F0704.png "Layout slots defined in a template")
397
398Seeing some code will clarify things further. To include a slot, use the `include_slot()` helper. The `has_slot()` helper returns `true` if the slot has been defined before, providing a fallback mechanism as a bonus. For instance, define a placeholder for a `'sidebar'` slot in the layout and its default content as shown in Listing 7-14.
399
400Listing 7-14 - Including a `'sidebar'` Slot in the Layout
401
402    [php]
403    <div id="sidebar">
404    <?php if (has_slot('sidebar')): ?>
405      <?php include_slot('sidebar') ?>
406    <?php else: ?>
407      <!-- default sidebar code -->
408      <h1>Contextual zone</h1>
409      <p>This zone contains links and information
410      relative to the main content of the page.</p>
411    <?php endif; ?>
412    </div>
413
414As it's quite common to display some default content if a slot is not defined, the `include_slot` helper returns a Boolean indicating if the slot has been defined. Listing 7-15 shows how to take this return value into account to simplify the code.
415
416Listing 7-15 - Including a `'sidebar'` Slot in the Layout
417
418    [php]
419    <div id="sidebar">
420    <?php if (!include_slot('sidebar')): ?>
421      <!-- default sidebar code -->
422      <h1>Contextual zone</h1>
423      <p>This zone contains links and information
424      relative to the main content of the page.</p>
425    <?php endif; ?>
426    </div>
427
428Each template has the ability to define the contents of a slot (actually, even partials can do it). As slots are meant to hold HTML code, symfony offers a convenient way to define them: just write the slot code between a call to the `slot()` and `end_slot()` helpers, as in Listing 7-16.
429
430Listing 7-16 - Overriding the `'sidebar'` Slot Content in a Template
431
432    [php]
433    // ...
434
435    <?php slot('sidebar') ?>
436      <!-- custom sidebar code for the current template-->
437      <h1>User details</h1>
438      <p>name:  <?php echo $user->getName() ?></p>
439      <p>email: <?php echo $user->getEmail() ?></p>
440    <?php end_slot() ?>
441
442The code between the slot helpers is executed in the context of the template, so it has access to all the variables that were defined in the action. Symfony will automatically put the result of this code in the response object. It will not be displayed in the template, but made available for future `include_slot()` calls, like the one in Listing 7-14.
443
444Slots are very useful to define zones meant to display contextual content. They can also be used to add HTML code to the layout for certain actions only. For instance, a template displaying the list of the latest news might want to add a link to an RSS feed in the `<head>` part of the layout. This is achieved simply by adding a `'feed'` slot in the layout and overriding it in the template of the list.
445
446If the content of the slot is very short, as this is the case when defining a `title` slot for example, you can simply pass the content as a second argument of the `slot()` method as shown in Listing 7-17.
447
448Listing 7-17 - Using `slot()` to define a short Value
449
450    [php]
451    <?php slot('title', 'The title value') ?>
452
453>**SIDEBAR**
454>Where to find template fragments
455>
456>People working on templates are usually web designers, who may not know symfony very well and may have difficulties finding template fragments, since they can be scattered all over the application. These few guidelines will make them more comfortable with the symfony templating system.
457>
458>First of all, although a symfony project contains many directories, all the layouts, templates, and template fragments files reside in directories named `templates/`. So as far as a web designer is concerned, a project structure can be reduced to something like this:
459>
460>
461>     myproject/
462>       apps/
463>         application1/
464>           templates/       # Layouts for application 1
465>           modules/
466>             module1/
467>               templates/   # Templates and partials for module 1
468>             module2/
469>               templates/   # Templates and partials for module 2
470>             module3/
471>               templates/   # Templates and partials for module 3
472>
473>
474>All other directories can be ignored.
475>
476>When meeting an `include_partial()`, web designers just need to understand that only the first argument is important. This argument's pattern is `module_name/partial_name`, and that means that the presentation code is to be found in `modules/module_name/templates/_partial_name.php`.
477>
478>For the `include_component()` helper, module name and partial name are the first two arguments. As for the rest, a general idea about what helpers are and which helpers are the most common in templates should be enough to start designing templates for symfony applications.
479
480View Configuration
481------------------
482
483In symfony, a view consists of two distinct parts:
484
485  * The HTML presentation of the action result (stored in the template, in the layout, and in the template fragments)
486  * All the rest, including the following:
487
488    * Meta declarations: Keywords, description, or cache duration.
489    * 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.
490    * File inclusions: JavaScript and style sheet files.
491    * Layout: Some actions require a custom layout (pop-ups, ads, and so on) or no layout at all (such as Ajax actions).
492
493In 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.
494
495>**NOTE**
496>If you ever set a view configuration parameter both via the `sfResponse` object and via the `view.yml` file, the `sfResponse` definition takes precedence.
497
498### The view.yml File
499
500Each 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-18 shows an example of view configuration.
501
502Listing 7-18 - Sample Module-Level `view.yml`
503
504    editSuccess:
505      metas:
506        title: Edit your profile
507
508    editError:
509      metas:
510        title: Error in the profile edition
511
512    all:
513      stylesheets: [my_style]
514      metas:
515        title: My website
516
517>**CAUTION**
518>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`.
519
520The 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:
521
522  * In `apps/frontend/modules/mymodule/config/view.yml`, the per-view definitions apply only to one view and override the module-level definitions.
523  * In `apps/frontend/modules/mymodule/config/view.yml`, the `all:` definitions apply to all the actions of the module and override the application-level definitions.
524  * In `apps/frontend/config/view.yml`, the `default:` definitions apply to all modules and all actions of the application.
525
526>**TIP**
527>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.
528
529After 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-19.
530
531Listing 7-19 - Default Application-Level View Configuration, in `apps/frontend/config/view.yml`
532
533    default:
534      http_metas:
535        content-type: text/html
536
537      metas:
538        #title:        symfony project
539        #description:  symfony project
540        #keywords:     symfony, project
541        #language:     en
542        robots:       index, follow
543
544      stylesheets:    [main]
545
546      javascripts:    []
547
548      has_layout:     on
549      layout:         layout
550
551Each of these settings will be described in detail in the "View Configuration Settings" section.
552
553### The Response Object
554
555Although 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-20 lists some of the `sfResponse` methods often used from within an action.
556
557Listing 7-20 - Actions Have Access to the `sfResponse` Object Methods
558
559    [php]
560    class mymoduleActions extends sfActions
561    {
562      public function executeIndex()
563      {
564        $response = $this->getResponse();
565
566        // HTTP headers
567        $response->setContentType('text/xml');
568        $response->setHttpHeader('Content-Language', 'en');
569        $response->setStatusCode(403);
570        $response->addVaryHttpHeader('Accept-Language');
571        $response->addCacheControlHttpHeader('no-cache');
572
573        // Cookies
574        $response->setCookie($name, $content, $expire, $path, $domain);
575
576        // Metas and page headers
577        $response->addMeta('robots', 'NONE');
578        $response->addMeta('keywords', 'foo bar');
579        $response->setTitle('My FooBar Page');
580        $response->addStyleSheet('custom_style');
581        $response->addJavaScript('custom_behavior');
582      }
583    }
584
585In addition to the setter methods shown here, the `sfResponse` class has getters that return the current value of the response attributes.
586
587The 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.
588
589    [php]
590    $response->setContentType('text/xml');
591    echo $response->getContentType();
592     => 'text/xml; charset=utf-8'
593
594The 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.
595
596    [php]
597    $response->setStatusCode(404, 'This page does not exist');
598
599>**TIP**
600>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.
601
602### View Configuration Settings
603
604You may have noticed that there are two kinds of view configuration settings:
605
606  * The ones that have a unique value (the value is a string in the `view.yml` file and the response uses a `set` method for those)
607  * The ones with multiple values (for which `view.yml` uses arrays and the response uses an `add` method)
608
609Keep 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.
610
611#### Meta Tag Configuration
612
613The 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-21, or with the `addHttpMeta()` and `addMeta()` response methods in the action, as in Listing 7-22.
614
615Listing 7-21 - Meta Definition As Key: Value Pairs in `view.yml`
616
617    http_metas:
618      cache-control: public
619
620    metas:
621      description:   Finance in France
622      keywords:      finance, France
623
624Listing 7-22 - Meta Definition As Response Settings in the Action
625
626    [php]
627    $this->getResponse()->addHttpMeta('cache-control', 'public');
628    $this->getResponse()->addMeta('description', 'Finance in France');
629    $this->getResponse()->addMeta('keywords', 'finance, France');
630
631Adding 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.
632
633    [php]
634    $this->getResponse()->addHttpMeta('accept-language', 'en');
635    $this->getResponse()->addHttpMeta('accept-language', 'fr', false);
636    echo $this->getResponse()->getHttpHeader('accept-language');
637     => 'en, fr'
638
639In 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-18) and the response attribute to output proper `<meta>` tags. The example in Listing 7-21 ends up as shown in Listing 7-23.
640
641Listing 7-23 - Meta Tags Output in the Final Page
642
643    [php]
644    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
645    <meta http-equiv="cache-control" content="public" />
646    <meta name="robots" content="index, follow" />
647    <meta name="description" content="Finance in France" />
648    <meta name="keywords" content="finance, France" />
649
650As 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`:
651
652    http_metas:
653      content-type: text/plain
654
655    has_layout: false
656
657#### Title Configuration
658
659The 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-24 shows the title definition in `view.yml`, and Listing 7-25 shows the definition in the action.
660
661Listing 7-24 - Title Definition in `view.yml`
662
663    indexSuccess:
664      metas:
665        title: Three little piggies
666
667Listing 7-25 - Title Definition in the Action--Allows for Dynamic Titles
668
669    [php]
670    $this->getResponse()->setTitle(sprintf('%d little piggies', $number));
671
672In 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.
673
674#### File Inclusion Configuration
675
676Adding a specific style sheet or JavaScript file to a view is easy, as Listing 7-26 demonstrates.
677
678Listing 7-26 - Asset File Inclusion
679
680    // In the view.yml
681    indexSuccess:
682      stylesheets: [mystyle1, mystyle2]
683      javascripts: [myscript]
684
685    [php]
686    // In the Action
687    $this->getResponse()->addStylesheet('mystyle1');
688    $this->getResponse()->addStylesheet('mystyle2');
689    $this->getResponse()->addJavascript('myscript');
690
691    // In the Template
692    <?php use_stylesheet('mystyle1') ?>
693    <?php use_stylesheet('mystyle2') ?>
694    <?php use_javascript('myscript') ?>
695    
696In 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.
697
698Unlike 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-27, whatever the content of the template and the layout.
699
700Listing 7-27 - File Inclusion Result--No Need for a Helper Call in the Layout
701
702    [php]
703    <head>
704    ...
705    <link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle1.css" />
706    <link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle2.css" />
707    <script language="javascript" type="text/javascript" src="/js/myscript.js">
708    </script>
709    </head>
710
711>**NOTE**
712>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.
713
714Remember 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-28, 7-29, and 7-30 demonstrate this principle.
715
716Listing 7-28 - Sample Application `view.yml`
717
718    default:
719      stylesheets: [main]
720
721Listing 7-29 - Sample Module `view.yml`
722
723    indexSuccess:
724      stylesheets: [special]
725
726    all:
727      stylesheets: [additional]
728
729Listing 7-30 - Resulting `indexSuccess` View
730
731    [php]
732    <link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
733    <link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" />
734    <link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" />
735
736If 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-31.
737
738Listing 7-31 - Sample Module `view.yml` That Removes a File Defined at the Application Level
739
740    indexSuccess:
741      stylesheets: [-main, special]
742
743    all:
744      stylesheets: [additional]
745
746To remove all style sheets or JavaScript files, use `-*` as a file name, as shown in Listing 7-32.
747
748Listing 7-32 - Sample Module `view.yml` That Removes all Files Defined at the Application Level
749
750    indexSuccess:
751      stylesheets: [-*]
752      javascripts: [-*]
753
754You can be more accurate and define an additional parameter to force the position where to include the file (first or last position), as shown in Listing 7-33. This works for boths style sheets and JavaScript files.
755
756Listing 7-33 - Defining the Position of the Included Asset
757
758    // In the view.yml
759    indexSuccess:
760      stylesheets: [special: { position: first }]
761
762    [php]
763    // In the action
764    $this->getResponse()->addStylesheet('special', 'first');
765    
766    // In the template
767    <?php use_stylesheet('special', 'first') ?>
768
769**New in symfony 1.1**: You can also decide to bypass the transformation of the asset file name, so that the resulting `<link>` or `<script>` tags refer to the exact location specified, as show in Listing 7-34.
770
771Listing 7-34 - Style Sheet Inclusion with Raw Name
772
773    // In the view.yml
774    indexSuccess:
775      stylesheets: [main, paper: { raw_name: true }]
776
777    [php]
778    // In the Action
779    $this->getResponse()->addStylesheet('main', '', array('raw_name' => true));
780    
781    // In the template
782    <?php use_stylesheet('main', '', array('raw_name' => true)) ?>
783    
784    // Resulting View
785    <link rel="stylesheet" type="text/css" href="main" />
786
787To specify media for a style sheet inclusion, you can change the default style sheet tag options, as shown in Listing 7-35.
788
789Listing 7-35 - Style Sheet Inclusion with Media
790
791    // In the view.yml
792    indexSuccess:
793      stylesheets: [main, paper: { media: print }]
794
795    [php]
796    // In the Action
797    $this->getResponse()->addStylesheet('paper', '', array('media' => 'print'));
798    
799    // In the template
800    <?php use_stylesheet('paper', '', array('media' => 'print')) ?>
801    
802    // Resulting View
803    <link rel="stylesheet" type="text/css" media="print" href="/css/paper.css" />
804
805#### Layout Configuration
806
807According 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.
808
809You have already seen that the default layout is `myproject/apps/frontend/templates/layout.php`. Additional layouts must be added in the same global `templates/` directory. If you want a view to use a `frontend/templates/my_layout.php` file, use the syntax shown in Listing 7-36.
810
811Listing 7-36 - Layout Definition 
812
813    // In view.yml
814    indexSuccess:
815      layout: my_layout
816
817    [php]
818    // In the action
819    $this->setLayout('my_layout');
820    
821    // In the template
822    <?php decorate_with('my_layout') ?>
823
824Some 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 Listing 7-37.
825
826Listing 7-37 - Layout Removal 
827
828    // In `view.yml`
829    indexSuccess:
830      has_layout: false
831    
832    [php]
833    // In the Action
834    $this->setLayout(false);
835    
836    // In the template
837    <?php decorate_with(false) ?>
838
839>**NOTE**
840>Ajax actions views have no layout by default.
841
842Component Slots
843---------------
844
845Combining the power of view components and view configuration brings a new perspective to view development: the component slot system. It is an alternative to slots focusing on reusability and layer separation. So component slots are more structured than slots, but a little slower to execute.
846
847Just like slots, component slots are named placeholders that you can declare in the view elements. The difference resides in the way the filling code is determined. For a slot, the code is set in another view element; for a component slot, the code results from the execution of a component, and the name of this component comes from the view configuration. You will understand component slots more clearly after seeing them in action.
848
849To set a component slot placeholder, use the `include_component_slot()` helper. This function expects a label as a parameter. For instance, suppose that the `layout.php` file of the application contains a contextual sidebar. Listing 7-38 shows how the component slot helper would be included.
850
851Listing 7-38 - Including a Component Slot with the Name `'sidebar'`
852
853    [php]
854    ...
855    <div id="sidebar">
856      <?php include_component_slot('sidebar') ?>
857    </div>
858
859Define the correspondence between the component slot label and a component name in the view configuration. For instance, set the default component for the `sidebar` component slot in the application `view.yml`, under the `components` header. The key is the component slot label; the value must be an array containing a module and a component name. Listing 7-39 shows an example.
860
861Listing 7-39 - Defining the Default `'sidebar'` Slot Component, in `frontend/config/view.yml`
862
863    default:
864      components:
865        sidebar:  [bar, default]
866
867So when the layout is executed, the `sidebar` component slot is filled with t…

Large files files are truncated, but you can click here to view the full file