Because of the routing system, you should use link helpers instead of regular <a> tags in your templates. Don't look at it as a hassle, but rather as an opportunity to keep your application clean and easy to maintain. Besides, link helpers offer a few very useful shortcuts that you don't want to miss.

You already know about the link_to() helper. It outputs an XHTML-compliant hyperlink, and it expects two parameters: the element that can be clicked and the internal URI of the resource to which it points. If, instead of a hyperlink, you want a button, use the button_to() helper. Forms also have a helper to manage the value of the action attribute. You will learn more about forms in the next chapter. Listing 9-7 shows some examples of link helpers.

Listing 9-7 - Link Helpers for <a>, <input>, and <form> Tags

// Hyperlink on a string
<?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
 => <a href="/routed/url/to/Finance_in_France">my article</a>

// Hyperlink on an image
<?php echo link_to(image_tag('read.gif'), 'article/read?title=Finance_in_France') ?>
 => <a href="/routed/url/to/Finance_in_France"><img src="/images/read.gif" /></a>

// Button tag
<?php echo button_to('my article', 'article/read?title=Finance_in_France') ?>
 => <input value="my article" type="button"onclick="document.location.href='/routed/url/to/Finance_in_France';" />

// Form tag
<?php echo form_tag('article/read?title=Finance_in_France') ?>
 => <form method="post" action="/routed/url/to/Finance_in_France" />

Link helpers can accept internal URIs as well as absolute URLs (starting with http://, and skipped by the routing system) and anchors. Note that in real-world applications, internal URIs are built with dynamic parameters. Listing 9-8 shows examples of all these cases.

Listing 9-8 - URLs Accepted by Link Helpers

// Internal URI
<?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
 => <a href="/routed/url/to/Finance_in_France">my article</a>

// Internal URI with dynamic parameters
<?php echo link_to('my article', 'article/read?title='.$article->getTitle()) ?>

// Internal URI with anchors
<?php echo link_to('my article', 'article/read?title=Finance_in_France#foo') ?>
 => <a href="/routed/url/to/Finance_in_France#foo">my article</a>

// Absolute URL
<?php echo link_to('my article', 'http://www.example.com/foobar.html') ?>
 => <a href="http://www.example.com/foobar.html">my article</a>

As explained in Chapter 7, helpers accept an additional options argument, which can be an associative array or a string. This is true for link helpers, too, as shown in Listing 9-9.

Listing 9-9 - Link Helpers Accept Additional Options

// Additional options as an associative array
<?php echo link_to('my article', 'article/read?title=Finance_in_France', array(
  'class'  => 'foobar',
  'target' => '_blank'
)) ?>

// Additional options as a string (same result)
<?php echo link_to('my article', 'article/read?title=Finance_in_France','class=foobar target=_blank') ?>
 => <a href="/routed/url/to/Finance_in_France" class="foobar" target="_blank">my article</a>

You can also add one of the symfony-specific options for link helpers: confirm and popup. The first one displays a JavaScript confirmation dialog box when the link is clicked, and the second opens the link in a new window, as shown in Listing 9-10.

Listing 9-10 - 'confirm' and 'popup' Options for Link Helpers

<?php echo link_to('delete item', 'item/delete?id=123', 'confirm=Are you sure?') ?>
 => <a onclick="return confirm('Are you sure?');"
       href="/routed/url/to/delete/123.html">delete item</a>

<?php echo link_to('add to cart', 'shoppingCart/add?id=100', 'popup=true') ?>
 => <a onclick="window.open(this.href);return false;"
       href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>

<?php echo link_to('add to cart', 'shoppingCart/add?id=100', array(
  'popup' => array('popupWindow', 'width=310,height=400,left=320,top=0')
)) ?>
 => <a onclick="window.open(this.href,'popupWindow','width=310,height=400,left=320,top=0');return false;"
       href="/fo_dev.php/shoppingCart/add/id/100.html">add to cart</a>

These options can be combined.

9.3.3. Fake GET and POST Options

Sometimes web developers use GET requests to actually do a POST. For instance, consider the following URL:

http://www.example.com/index.php/shopping_cart/add/id/100

This request will change the data contained in the application, by adding an item to a shopping cart object, stored in the session or in a database. This URL can be bookmarked, cached, and indexed by search engines. Imagine all the nasty things that might happen to the database or to the metrics of a website using this technique. As a matter of fact, this request should be considered as a POST, because search engine robots do not do POST requests on indexing.

Symfony provides a way to transform a call to a link_to() or button_to() helper into an actual POST. Just add a post=true option, as shown in Listing 9-11.

Listing 9-11 - Making a Link Call a POST Request

<?php echo link_to('go to shopping cart', 'shoppingCart/add?id=100', 'post=true') ?>
 => <a onclick="f = document.createElement('form'); document.body.appendChild(f);
                f.method = 'POST'; f.action = this.href; f.submit();return false;"
       href="/shoppingCart/add/id/100.html">go to shopping cart</a>

This <a> tag has an href attribute, and browsers without JavaScript support, such as search engine robots, will follow the link doing the default GET. So you must also restrict your action to respond only to the POST method, by adding something like the following at the beginning of the action:

$this->forward404Unless($this->getRequest()->isMethod('post'));

Just make sure you don't use this option on links located in forms, since it generates its own <form> tag.

It is a good habit to tag as POST the links that actually post data.

9.3.4. Forcing Request Parameters As GET Variables

According to your routing rules, variables passed as parameters to a link_to() are transformed into patterns. If no rule matches the internal URI in the routing.yml file, the default rule transforms module/action?key=value into /module/action/key/value, as shown in Listing 9-12.

Listing 9-12 - Default Routing Rule

<?php echo link_to('my article', 'article/read?title=Finance_in_France') ?>
=> <a href="/article/read/title/Finance_in_France">my article</a>

If you actually need to keep the GET syntax — to have request parameters passed under the ?key=value form — you should put the variables that need to be forced outside the URL parameter, in the query_string option. As this would conflict also with an anchor in the URL, you have to put it into the anchor option instead of prepending it to the internal URI. All the link helpers accept these options, as demonstrated in Listing 9-13.

Listing 9-13 - Forcing GET Variables with the query_string Option

<?php echo link_to('my article', 'article/read', array(
  'query_string' => 'title=Finance_in_France',
  'anchor' => 'foo'
)) ?>
=> <a href="/article/read?title=Finance_in_France#foo">my article</a>

A URL with request parameters appearing as GET variables can be interpreted by a script on the client side, and by the $_GET and $_REQUEST variables on the server side.

9.3.5. Using Absolute Paths

The link and asset helpers generate relative paths by default. To force the output to absolute paths, set the absolute option to true, as shown in Listing 9-14. This technique is useful for inclusions of links in an e-mail message, RSS feed, or API response.

Listing 9-14 - Getting Absolute URLs Instead of Relative URLs

<?php echo url_for('article/read?title=Finance_in_France') ?>
 => '/routed/url/to/Finance_in_France'
<?php echo url_for('article/read?title=Finance_in_France', true) ?>
 => 'http://www.example.com/routed/url/to/Finance_in_France'

<?php echo link_to('finance', 'article/read?title=Finance_in_France') ?>
 => <a href="/routed/url/to/Finance_in_France">finance</a>
<?php echo link_to('finance', 'article/read?title=Finance_in_France','absolute=true') ?>
 => <a href=" http://www.example.com/routed/url/to/Finance_in_France">finance</a>

// The same goes for the asset helpers
<?php echo image_tag('test', 'absolute=true') ?>
<?php echo javascript_include_tag('myscript', 'absolute=true') ?>