India : 011-32914285 | Outside :+91-11-32914285
India : 09310894285 | Outside :+ 91- 9310894284
 Office Hours: M-F 9:30AM - 5:30PM CST
Email  ::bageshsingh@gmail.com 

 

Bagesh Singh Web Developer

the shortest distance to a classifieds and auction software solution

 

Flower Galaxy India

Flower Galaxy India

This site is a fully shopping cart  which have good design and good class based programming used to create it.


<< >> Play > Stop

Symfony

What can symfony do for you? What's required to use it? This chapter answers these questions.

 

Symfony in Brief

 

A framework streamlines application development by automating many of the patterns employed for a given purpose. A framework also adds structure to the code, prompting the developer to write better, more readable, and more maintainable code. Ultimately, a framework makes programming easier, since it packages complex operations into simple statements.

 

Symfony is a complete framework designed to optimize the development of web applications by way of several key features. For starters, it separates a web application's business rules, server logic, and presentation views. It contains numerous tools and classes aimed at shortening the development time of a complex web application. Additionally, it automates common tasks so that the developer can focus entirely on the specifics of an application. The end result of these advantages means there is no need to reinvent the wheel every time a new web application is built!

 

Symfony is written entirely in PHP 5. It has been thoroughly tested in various real-world projects, and is actually in use for high-demand e-business websites. It is compatible with most of the available databases engines, including MySQL, PostgreSQL, Oracle, and Microsoft SQL Server. It runs on *nix and Windows platforms. Let's begin with a closer look at its features.

 

Symfony Features

 

Symfony was built in order to fulfill the following requirements:

 

    * Easy to install and configure on most platforms (and guaranteed to work on standard *nix and Windows platforms)

 

    * Database engine-independent

 

    * Simple to use, in most cases, but still flexible enough to adapt to complex cases

 

    * Based on the premise of convention over configuration--the developer needs to configure only the unconventional

 

    * Compliant with most web best practices and design patterns

 

    * Enterprise-ready--adaptable to existing information technology (IT) policies and architectures, and stable enough for long-term projects

 

    * Very readable code, with phpDocumentor comments, for easy maintenance

 

    * Easy to extend, allowing for integration with other vendor libraries

 

Automated Web Project Features

 

Most of the common features of web projects are automated within symfony, as follows:

 

    * The built-in internationalization layer allows for both data and interface translation, as well as content localization.

 

    * The presentation uses templates and layouts that can be built by HTML designers without any knowledge of the framework. Helpers reduce the amount of presentation code to write by encapsulating large portions of code in simple function calls.

 

    * Forms support automated validation and repopulation, and this ensures a good quality of data in the database and a better user experience.

 

    * Output escaping protects applications from attacks via corrupted data.

 

    * The cache management features reduce bandwidth usage and server load.

 

    * Authentication and credential features facilitate the creation of restricted sections and user security management.

 

    * Routing and smart URLs make the page address part of the interface and search-engine friendly.

 

    * Built-in e-mail and API management features allow web applications to go beyond the classic browser interactions.

 

    * Lists are more user-friendly thanks to automated pagination, sorting, and filtering.

 

    * Factories, plug-ins, and mixins provide a high level of extensibility.

 

    * Ajax interactions are easy to implement thanks to one-line helpers that encapsulate cross-browser-compatible JavaScript effects.

 

Development Environment and Tools

 

To fulfill the requirements of enterprises having their own coding guidelines and project management rules, symfony can be entirely customized. It provides, by default, several development environments and is bundled with multiple tools that automate common software-engineering tasks:

 

    * The code-generation tools are great for prototyping and one-click back-end administration.

 

    * The built-in unit and functional testing framework provides the perfect tools to allow test-driven development.

 

    * The debug panel accelerates debugging by displaying all the information the developer needs on the page he's working on.

 

    * The command-line interface automates application deployment between two servers.

 

    * Live configuration changes are possible and effective.

 

    * The logging features give administrators full details about an application's activities.

 

Who Made Symfony and Why?

 

The first version of symfony was released in October 2005 by project founder Fabien Potencier, coauthor of this book. Fabien is the CEO of Sensio (http://www.sensio.com/), a French web agency well known for its innovative views on web development.

 

Back in 2003, Fabien spent some time inquiring about the existing open source development tools for web applications in PHP. He found that none fulfilled the previously described requirements. When PHP 5 was released, he decided that the available tools had reached a mature enough stage to be integrated into a full-featured framework. He subsequently spent a year developing the symfony core, basing his work on the Mojavi Model-View-Controller (MVC) framework, the Propel object-relational mapping (ORM), and the Ruby on Rails templating helpers.

 

Fabien originally built symfony for Sensio's projects, because having an effective framework at your disposal presents an ideal way to develop applications faster and more efficiently. It also makes web development more intuitive, and the resulting applications are more robust and easier to maintain. The framework entered the proving grounds when it was employed to build an e-commerce website for a lingerie retailer, and subsequently was applied to other projects.

 

After successfully using symfony for a few projects, Fabien decided to release it under an open source license. He did so to donate this work to the community, to benefit from user feedback, to showcase Sensio's experience, and because it's fun.

 

    Why "symfony" and not "FooBarFramework"? Because Fabien wanted a short name containing an s, as in Sensio, and an f, as in framework--easy to remember and not associated with another development tool. Also, he doesn't like capital letters. symfony was close enough, even if not completely English, and it was also available as a project name. The other alternative was "baguette".

 

For symfony to be a successful open source project, it needed to have extensive documentation, in English, to increase the adoption rate. Fabien asked fellow Sensio employee François Zaninotto, the other author of this book, to dig into the code and write an online book about it. It took quite a while, but when the project was made public, it was documented well enough to appeal to numerous developers. The rest is history.

 

The Symfony Community

 

As soon as the symfony website (http://www.symfony-project.org/) was launched, numerous developers from around the world downloaded and installed the framework, read the online documentation, and built their first application with symfony, and the buzz began to mount.

 

Web application frameworks were getting popular at that time, and the need for a full-featured framework in PHP was high. Symfony offered a compelling solution due to its impressive code quality and significant amount of documentation--two major advantages over the other players in the framework category. Contributors soon began to surface, proposing patches and enhancements, proofreading the documentation, and performing other much-needed roles.

 

The public source repository and ticketing system offer a variety of ways to contribute, and all volunteers are welcome. Fabien is still the main committer in the trunk of the source code repository, and guarantees the quality of the code.

 

Today, the symfony forum, mailing lists, and Internet Relay Chat (IRC) channel offer ideal support outlets, with seemingly each question getting an average of four answers. Newcomers install symfony every day, and the wiki and code snippets sections host a lot of user-contributed documentation. The number of known symfony applications increases by an average of five per week, and counting.

 

The symfony community is the third strength of the framework, and we hope that you will join it after reading this book.

 

Is Symfony for Me?

 

Whether you are a PHP 5 expert or a newcomer to web application programming, you will be able to use symfony. The main factor in deciding whether or not to do so is the size of your project.

 

If you want to develop a simple website with five to ten pages, limited access to a database, and no obligations to ensuring its performance or providing documentation, then you should stick with PHP alone. You wouldn't gain much from a web application framework, and using object orientation or an MVC model would likely only slow down your development process. As a side note, symfony is not optimized to run efficiently on a shared server where PHP scripts can run only in Common Gateway Interface (CGI) mode.

 

On the other hand, if you develop more complex web applications, with heavy business logic, PHP alone is not enough. If you plan on maintaining or extending your application in the future, you will need your code to be lightweight, readable, and effective. If you want to use the latest advances in user interaction (like Ajax) in an intuitive way, you can't just write hundreds of lines of JavaScript. If you want to have fun and develop fast, then PHP alone will probably be disappointing. In all these cases, symfony is for you.

 

And, of course, if you are a professional web developer, you already know all the benefits of web application frameworks, and you need one that is mature, well documented, and has a large community. Search no more, for symfony is your solution.

 

    If you would like a visual demonstration, take a look at the screencasts available from the symfony website. You will see how fast and fun it is to develop applications with symfony.

 

Fundamental Concepts

 

Before you get started with symfony, you should understand a few basic concepts. Feel free to skip ahead if you already know the meaning of OOP, ORM, RAD, DRY, KISS, TDD, YAML, and PEAR.

 

PHP 5

 

Symfony is developed in PHP 5 (http://www.php.net/) and dedicated to building web applications with the same language. Therefore, a solid understanding of PHP 5 is required to get the most out of the framework.

 

Developers who already know PHP 4 but not PHP 5 should mainly focus on the language's new object-oriented model.

 

Object-Oriented Programming (OOP)

 

Object-oriented programming (OOP) will not be explained in this chapter. It needs a whole book itself! Because symfony makes extensive use of the object-oriented mechanisms available as of PHP 5, OOP is a prerequisite to learning symfony.

 

Wikipedia explains OOP as follows:

 

The idea behind object-oriented programming is that a computer program may be seen as comprising a collection of individual units, or objects, that act on each other, as opposed to a traditional view in which a program may be seen as a collection of functions, or simply as a list of instructions to the computer.

 

PHP 5 implements the object-oriented paradigms of class, object, method, inheritance, and much more. Those who are not familiar with these concepts are advised to read the related PHP documentation, available at http://www.php.net/manual/en/language.oop5.basic.php.

 

Magic Methods

 

One of the strengths of PHP's object capabilities is the use of magic methods. These are methods that can be used to override the default behavior of classes without modifying the outside code. They make the PHP syntax less verbose and more extensible. They are easy to recognize, because the names of the magic methods start with two underscores (__).

 

For instance, when displaying an object, PHP implicitly looks for a __toString() method for this object to see if a custom display format was defined by the developer:

 

$myObject = new myClass();

 

echo $myObject;

 

// Will look for a magic method

 

echo $myObject->__toString();

 

Symfony uses magic methods, so you should have a thorough understanding of them. They are described in the PHP documentation (http://www.php.net/manual/en/language.oop5.magic.php).

 

PHP Extension and Application Repository (PEAR)

 

PEAR is "a framework and distribution system for reusable PHP components". PEAR allows you to download, install, upgrade, and uninstall PHP scripts. When using a PEAR package, you don't need to worry about where to put scripts, how to make them available, or how to extend the command-line interface (CLI).

 

PEAR is a community-driven project written in PHP and shipped with standard PHP distributions.

 

    The PEAR website, http://pear.php.net/, provides documentation and packages grouped by categories.

 

PEAR is the most professional way to install vendor libraries in PHP. Symfony advises the use of PEAR to keep a central installation point for use across multiple projects. The symfony plug-ins are PEAR packages with a special configuration. The symfony framework itself is available as a PEAR package.

 

You don't need to know all about the PEAR syntax to use symfony. You just need to understand what it does and have it installed. You can check that PEAR is installed in your computer by typing the following in a CLI:

 

> pear info pear

 

This command will return the version number of your PEAR installation.

 

The symfony project has its own PEAR repository, or channel. Note that channels are available only since version 1.4.0 of PEAR, so you should upgrade if your version is older. To upgrade your version of PEAR, issue the following command:

 

> pear upgrade PEAR

 

Object-Relational Mapping (ORM)

 

Databases are relational. PHP 5 and symfony are object-oriented. In order to access the database in an object-oriented way, an interface translating the object logic to the relational logic is required. This interface is called an object-relational mapping, or ORM.

 

An ORM is made up of objects that give access to data and keep business rules within themselves.

 

One benefit of an object/relational abstraction layer is that it prevents you from using a syntax that is specific to a given database. It automatically translates calls to the model objects to SQL queries optimized for the current database.

 

This means that switching to another database system in the middle of a project is easy. Imagine that you have to write a quick prototype for an application, but the client has not decided yet which database system would best suit his needs. You can start building your application with SQLite, for instance, and switch to MySQL, PostgreSQL, or Oracle when the client is ready to decide. Just change one line in a configuration file, and it works.

 

An abstraction layer encapsulates the data logic. The rest of the application does not need to know about the SQL queries, and the SQL that accesses the database is easy to find. Developers who specialize in database programming also know clearly where to go.

 

Using objects instead of records, and classes instead of tables, has another benefit: you can add new accessors to your tables. For instance, if you have a table called Client with two fields, FirstName and LastName, you might like to be able to require just a Name. In an object-oriented world, this is as easy as adding a new accessor method to the Client class, like this:

 

public function getName()

 

{

 

  return $this->getFirstName().' '.$this->getLastName();

 

}

 

All the repeated data-access functions and the business logic of the data can be maintained within such objects. For instance, consider a class ShoppingCart in which you keep items (which are objects). To retrieve the full amount of the shopping cart for the checkout, you can add a getTotal() method, like this:

 

public function getTotal()

 

{

 

  $total = 0;

 

  foreach ($this->getItems() as $item)

 

  {

 

    $total += $item->getPrice() * $item->getQuantity();

 

  }

 

  return $total;

 

}

 

Using this method we are able to control the values returned from an object level. Imagine if later there is a decision to add some discount logic which affects the total - it can simply be added to the getTotal() method or even to the getPrice() methods of the items and the correct value would be returned.

 

Propel, another open source project, is currently one of the best object/relational abstraction layers for PHP 5. Symfony integrates Propel seamlessly into the framework, so most of the data manipulation described in this book follows the Propel syntax. This book will describe how to use the Propel objects, but for a more complete reference, a visit to the Propel website (http://propel.phpdb.org/trac/) is recommended.

 

Rapid Application Development (RAD)

 

Programming web applications has long been a tedious and slow job. Following the usual software engineering life cycles (like the one proposed by the Rational Unified Process, for instance), the development of web applications could not start before a complete set of requirements was written, a lot of Unified Modeling Language (UML) diagrams were drawn, and tons of preliminary documentation were produced. This was due to the general speed of development, to the lack of versatility of programming languages (you had to build, compile, restart, and who knows what else before actually seeing your program run), and most of all, to the fact that clients were quite reasonable and didn't change their minds constantly.

 

Today, business moves faster, and clients tend to constantly change their minds in the course of the project development. Of course, they expect the development team to adapt to their needs and modify the structure of an application quickly. Fortunately, the use of scripting languages like Perl and PHP makes it easy to apply other programming strategies, such as rapid application development (RAD) or agile software development.

 

One of the ideas of these methodologies is to start developing as soon as possible so that the client can review a working prototype and offer additional direction. Then the application gets built in an iterative process, releasing increasingly feature-rich versions in short development cycles.

 

The consequences for the developer are numerous. A developer doesn't need to think about the future when implementing a feature. The method used should be as simple and straightforward as possible. This is well illustrated by the maxim of the KISS principle: Keep It Simple, Stupid.

 

When the requirements evolve or when a feature is added, existing code usually has to be partly rewritten. This process is called refactoring, and happens a lot in the course of a web application development. Code is moved to other places according to its nature. Duplicated portions of code are refactored to a single place, thus applying the Don't Repeat Yourself (DRY) principle.

 

And to make sure that the application still runs when it changes constantly, it needs a full set of unit tests that can be automated. If well written, unit tests are a solid way to ensure that nothing is broken by adding or refactoring code. Some development methodologies even stipulate writing tests before coding--that's called test-driven development (TDD).

 

    There are many other principles and good habits related to agile development. One of the most effective agile development methodologies is called Extreme Programming (abbreviated as XP), and the XP literature will teach you a lot about how to develop an application in a fast and effective way. A good starting place is the XP series books by Kent Beck (Addison-Wesley).

 

Symfony is the perfect tool for RAD. As a matter of fact, the framework was built by a web agency applying the RAD principle for its own projects. This means that learning to use symfony is not about learning a new language, but more about applying the right reflexes and the best judgment in order to build applications in a more effective way.

 

The symfony project website proposes a step-by-step tutorial illustrating the development of an application in an agile way. It is called askeet (http://www.symfony-project.org/askeet), and is recommended reading for those who want to learn more about agile development.

 

YAML

 

According to the official YAML website (http://www.yaml.org/), YAML is "a straightforward machine parsable data serialization format designed for human readability and interaction with scripting languages". Put another way, YAML is a very simple language used to describe data in an XML-like way but with a much simpler syntax. It is especially useful to describe data that can be translated into arrays and hashes, like this:

 

$house = array(

 

  'family' => array(

 

    'name'     => 'Doe',

 

    'parents'  => array('John', 'Jane'),

 

    'children' => array('Paul', 'Mark', 'Simone')

 

  ),

 

  'address' => array(

 

    'number'   => 34,

 

    'street'   => 'Main Street',

 

    'city'     => 'Nowheretown',

 

    'zipcode'  => '12345'

 

  )

 

);

 

This PHP array can be automatically created by parsing the YAML string:

 

house:

 

  family:

 

    name:     Doe

 

    parents:

 

      - John

 

      - Jane

 

    children:

 

      - Paul

 

      - Mark

 

      - Simone

 

  address:

 

    number: 34

 

    street: Main Street

 

    city: Nowheretown

 

    zipcode: "12345"

 

In YAML, structure is shown through indentation, sequence items are denoted by a dash, and key/value pairs within a map are separated by a colon. YAML also has a shorthand syntax to describe the same structure with fewer lines, where arrays are explicitly shown with [] and hashes with {}. Therefore, the previous YAML data can be written in a shorter way, as follows:

 

house:

 

  family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }

 

  address: { number: 34, street: Main Street, city: Nowheretown, zipcode: "12345" }

 

YAML is an acronym for "YAML Ain't Markup Language" and pronounced "yamel". The format has been around since 2001, and YAML parsers exist for a large variety of languages.

 

    The specifications of the YAML format are available at http://www.yaml.org/.

 

As you can see, YAML is much faster to write than XML (no more closing tags or explicit quotes), and it is more powerful than .ini files (which don't support hierarchy). That is why symfony uses YAML as the preferred language to store configuration. You will see a lot of YAML files in this book, but it is so straightforward that you probably don't need to learn more about it.

 

Summary

 

Symfony is a PHP 5 web application framework. It adds a new layer on top of the PHP language, providing tools that speed up the development of complex web applications. This book will tell you all about it, and you just need to be familiar with the basic concepts of modern programming to understand it--namely object-oriented programming (OOP), object-relational mapping (ORM), and rapid application development (RAD). The only required technical background is knowledge of PHP 5.

At first glance, the code behind a symfony-driven application can seem quite daunting. It consists of many directories and scripts, and the files are a mix of PHP classes, HTML, and even an intermingling of the two. You'll also see references to classes that are otherwise nowhere to be found within the application folder, and the directory depth stretches to six levels. But once you understand the reason behind all of this seeming complexity, you'll suddenly feel like it's so natural that you wouldn't trade the symfony application structure for any other. This chapter explains away that intimidated feeling.
The MVC Pattern

Symfony is based on the classic web design pattern known as the MVC architecture, which consists of three levels:

    * The model represents the information on which the application operates--its business logic.
    * The view renders the model into a web page suitable for interaction with the user.
    * The controller responds to user actions and invokes changes on the model or view as appropriate.

Figure 2-1 illustrates the MVC pattern.

The MVC architecture separates the business logic (model) and the presentation (view), resulting in greater maintainability. For instance, if your application should run on both standard web browsers and handheld devices, you just need a new view; you can keep the original controller and model. The controller helps to hide the detail of the protocol used for the request (HTTP, console mode, mail, and so on) from the model and the view. And the model abstracts the logic of the data, which makes the view and the action independent of, for instance, the type of database used by the application.

Figure 2-1 - The MVC pattern

The MVC pattern
MVC Layering

To help you understand MVC's advantages, let's see how to convert a basic PHP application to an MVC-architectured application. A list of posts for a weblog application will be a perfect example.
Flat Programming

In a flat PHP file, displaying a list of database entries might look like the script presented in Listing 2-1.

Listing 2-1 - A Flat Script

<?php
 
// Connecting, selecting database
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);
 
// Performing SQL query
$result = mysql_query('SELECT date, title FROM post', $link);
 
?>
 
<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
   <h1>List of Posts</h1>
   <table>
     <tr><th>Date</th><th>Title</th></tr>
<?php
// Printing results in HTML
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>
 
<?php
 
// Closing connection
mysql_close($link);
 
?>

That's quick to write, fast to execute, and impossible to maintain. The following are the major problems with this code:

    * There is no error-checking (what if the connection to the database fails?).
    * HTML and PHP code are mixed, even interwoven together.
    * The code is tied to a MySQL database.

Isolating the Presentation

The echo and printf calls in Listing 2-1 make the code difficult to read. Modifying the HTML code to enhance the presentation is a hassle with the current syntax. So the code can be split into two parts. First, the pure PHP code with all the business logic goes in a controller script, as shown in Listing 2-2.

Listing 2-2 - The Controller Part, in index.php

<?php
 
 // Connecting, selecting database
 $link = mysql_connect('localhost', 'myuser', 'mypassword');
 mysql_select_db('blog_db', $link);
 
 // Performing SQL query
 $result = mysql_query('SELECT date, title FROM post', $link);
 
 // Filling up the array for the view
 $posts = array();
 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
 {
    $posts[] = $row;
 }
 
 // Closing connection
 mysql_close($link);
 
 // Requiring the view
 require('view.php');
 
 ?>

The HTML code, containing template-like PHP syntax, is stored in a view script, as shown in Listing 2-3.

Listing 2-3 - The View Part, in view.php

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

A good rule of thumb to determine whether the view is clean enough is that it should contain only a minimum amount of PHP code, in order to be understood by an HTML designer without PHP knowledge. The most common statements in views are echo, if/endif, foreach/endforeach, and that's about all. Also, there should not be PHP code echoing HTML tags.

All the logic is moved to the controller script, and contains only pure PHP code, with no HTML inside. As a matter of fact, you should imagine that the same controller could be reused for a totally different presentation, perhaps in a PDF file or an XML structure.
Isolating the Data Manipulation

Most of the controller script code is dedicated to data manipulation. But what if you need the list of posts for another controller, say one that would output an RSS feed of the weblog posts? What if you want to keep all the database queries in one place, to avoid code duplication? What if you decide to change the data model so that the post table gets renamed weblog_post? What if you want to switch to PostgreSQL instead of MySQL? In order to make all that possible, you need to remove the data-manipulation code from the controller and put it in another script, called the model, as shown in Listing 2-4.

Listing 2-4 - The Model Part, in model.php

<?php
 
function getAllPosts()
{
  // Connecting, selecting database
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);
 
  // Performing SQL query
  $result = mysql_query('SELECT date, title FROM post', $link);
 
  // Filling up the array
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }
 
  // Closing connection
  mysql_close($link);
 
  return $posts;
}
 
?>

The revised controller is presented in Listing 2-5.

Listing 2-5 - The Controller Part, Revised, in index.php

<?php
 
// Requiring the model
require_once('model.php');
 
// Retrieving the list of posts
$posts = getAllPosts();
 
// Requiring the view
require('view.php');
 
?>

The controller becomes easier to read. Its sole task is to get the data from the model and pass it to the view. In more complex applications, the controller also deals with the request, the user session, the authentication, and so on. The use of explicit names for the functions of the model even makes code comments unnecessary in the controller.

The model script is dedicated to data access and can be organized accordingly. All parameters that don't depend on the data layer (like request parameters) must be given by the controller and not accessed directly by the model. The model functions can be easily reused in another controller.
Layer Separation Beyond MVC

So the principle of the MVC architecture is to separate the code into three layers, according to its nature. Data logic code is placed within the model, presentation code within the view, and application logic within the controller.

Other additional design patterns can make the coding experience even easier. The model, view, and controller layers can be further subdivided.
Database Abstraction

The model layer can be split into a data access layer and a database abstraction layer. That way, data access functions will not use database-dependent query statements, but call some other functions that will do the queries themselves. If you change your database system later, only the database abstraction layer will need updating.

A sample database abstraction layer is presented in Listing 2-6, followed by an example of a MySQL-specific data access layer in Listing 2-7.

Listing 2-6 - The Database Abstraction Part of the Model

<?php
 
function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}
 
function close_connection($link)
{
  mysql_close($link);
}
 
function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);
 
  return mysql_query($query, $link);
}
 
function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

Listing 2-7 - The Data Access Part of the Model

function getAllPosts()
{
  // Connecting to database
  $link = open_connection('localhost', 'myuser', 'mypassword');
 
  // Performing SQL query
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);
 
  // Filling up the array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }
 
  // Closing connection
  close_connection($link);
 
  return $posts;
}
 
?>

You can check that no database-engine dependent functions can be found in the data access layer, making it database-independent. Additionally, the functions created in the database abstraction layer can be reused for many other model functions that need access to the database.

    The examples in Listings 2-6 and 2-7 are still not very satisfactory, and there is some work left to do to have a full database abstraction (abstracting the SQL code through a database-independent query builder, moving all functions into a class, and so on). But the purpose of this book is not to show you how to write all that code by hand, and you will see in Chapter 8 that symfony natively does all the abstraction very well.

View Elements

The view layer can also benefit from some code separation. A web page often contains consistent elements throughout an application: the page headers, the graphical layout, the footer, and the global navigation. Only the inner part of the page changes. That's why the view is separated into a layout and a template. The layout is usually global to the application, or to a group of pages. The template only puts in shape the variables made available by the controller. Some logic is needed to make these components work together, and this view logic layer will keep the name view. According to these principles, the view part of Listing 2-3 can be separated into three parts, as shown in Listings 2-8, 2-9, and 2-10.

Listing 2-8 - The Template Part of the View, in mytemplate.php

<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-9 - The View Logic Part of the View

<?php
 
$title = 'List of Posts';
$posts = getAllPosts();

Listing 2-10 - The Layout Part of the View

<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php include('mytemplate.php'); ?>
  </body>
</html>

Action and Front Controller

The controller doesn't do much in the previous example, but in real web applications, the controller has a lot of work. An important part of this work is common to all the controllers of the application. The common tasks include request handling, security handling, loading the application configuration, and similar chores. This is why the controller is often divided into a front controller, which is unique for the whole application, and actions, which contain only the controller code specific to one page.

One of the great advantages of a front controller is that it offers a unique entry point to the whole application. If you ever decide to close the access to the application, you will just need to edit the front controller script. In an application without a front controller, each individual controller would need to be turned off.
Object Orientation

All the previous examples use procedural programming. The OOP capabilities of modern languages make the programming even easier, since objects can encapsulate logic, inherit from one another, and provide clean naming conventions.

Implementing an MVC architecture in a language that is not object-oriented raises namespace and code-duplication issues, and the overall code is difficult to read.

Object orientation allows developers to deal with such things as the view object, the controller object, and the model classes, and to transform all the functions in the previous examples into methods. It is a must for MVC architectures.

    If you want to learn more about design patterns for web applications in an object-oriented context, read Patterns of Enterprise Application Architecture by Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0). Code examples in Fowler's book are in Java or C#, but are still quite readable for a PHP developer.

Symfony's MVC Implementation

Hold on a minute. For a single page listing the posts in a weblog, how many components are required? As illustrated in Figure 2-2, we have the following parts:

    * Model layer
          o Database abstraction
          o Data access
    * View layer
          o View
          o Template
          o Layout
    * Controller layer
          o Front controller
          o Action

Seven scripts--a whole lot of files to open and to modify each time you create a new page! However, symfony makes things easy. While taking the best of the MVC architecture, symfony implements it in a way that makes application development fast and painless.

First of all, the front controller and the layout are common to all actions in an application. You can have multiple controllers and layouts, but you need only one of each. The front controller is pure MVC logic component, and you will never need to write a single one, because symfony will generate it for you.

The other good news is that the classes of the model layer are also generated automatically, based on your data structure. This is the job of the Propel library, which provides class skeletons and code generation. If Propel finds foreign key constraints or date fields, it will provide special accessor and mutator methods that will make data manipulation a piece of cake. And the database abstraction is totally invisible to you, because it is dealt with by another component, called Creole. So if you decide to change your database engine at one moment, you have zero code to rewrite. You just need to change one configuration parameter.

And the last thing is that the view logic can be easily translated as a simple configuration file, with no programming needed.

Figure 2-2 - Symfony workflow

Symfony workflow

That means that the list of posts described in our example would require only three files to work in symfony, as shown in Listings 2-11, 2-12, and 2-13.

Listing 2-11 - list Action, in myproject/apps/myapp/modules/weblog/actions/actions.class.php

<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}
 
?>

Listing 2-12 - list Template, in myproject/apps/myapp/modules/weblog/templates/listSuccess.php

<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-13 - list View, in myproject/apps/myapp/modules/weblog/config/view.yml

listSuccess:
  metas: { title: List of Posts }

In addition, you will still need to define a layout, as shown in Listing 2-14, but it will be reused many times.

Listing 2-14 - Layout, in myproject/apps/myapp/templates/layout.php

<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>

And that is really all you need. This is the exact code required to display the very same page as the flat script shown earlier in Listing 2-1. The rest (making all the components work together) is handled by symfony. If you count the lines, you will see that creating the list of posts in an MVC architecture with symfony doesn't require more time or coding than writing a flat file. Nevertheless, it gives you huge advantages, notably clear code organization, reusability, flexibility, and much more fun. And as a bonus, you have XHTML conformance, debug capabilities, easy configuration, database abstraction, smart URL routing, multiple environments, and many more development tools.
Symfony Core Classes

The MVC implementation in symfony uses several classes that you will meet quite often in this book:

    * sfController is the controller class. It decodes the request and hands it to the action.
    * sfRequest stores all the request elements (parameters, cookies, headers, and so on).
    * sfResponse contains the response headers and contents. This is the object that will eventually be converted to an HTML response and be sent to the user.
    * The context singleton (retrieved by sfContext::getInstance()) stores a reference to all the core objects and the current configuration; it is accessible from everywhere.

You will learn more about these objects in Chapter 6.

As you can see, all the symfony classes use the sf prefix, as do the symfony core variables in the templates. This should avoid name collisions with your own classes and variables, and make the core framework classes sociable and easy to recognize.

    Among the coding standards used in symfony, UpperCamelCase is the standard for class and variable naming. Two exceptions exist: core symfony classes start with sf, which is lowercase, and variables found in templates use the underscore-separated syntax.

Code Organization

Now that you know the different components of a symfony application, you're probably wondering how they are organized. Symfony organizes code in a project structure and puts the project files into a standard tree structure.
Project Structure: Applications, Modules, and Actions

In symfony, a project is a set of services and operations available under a given domain name, sharing the same object model.

Inside a project, the operations are grouped logically into applications. An application can normally run independently of the other applications of the same project. In most cases, a project will contain two applications: one for the front-office and one for the back-office, sharing the same database. But you can also have one project containing many mini-sites, with each site as a different application. Note that hyperlinks between applications must be in the absolute form.

Each application is a set of one or more modules. A module usually represents a page or a group of pages with a similar purpose. For example, you might have the modules home, articles, help, shoppingCart, account, and so on.

Modules hold actions, which represent the various actions that can be done in a module. For example, a shoppingCart module can have add, show, and update actions. Generally, actions can be described by a verb. Dealing with actions is almost like dealing with pages in a classic web application, although two actions can result in the same page (for instance, adding a comment to a post in a weblog will redisplay the post with the new comment).

    If this represents too many levels for a beginning project, it is very easy to group all actions into one single module, so that the file structure can be kept simple. When the application gets more complex, it will be time to organize actions into separate modules. As mentioned in Chapter 1, rewriting code to improve its structure or readability (but preserving its behavior) is called refactoring, and you will do this a lot when applying RAD principles.

Figure 2-3 shows a sample code organization for a weblog project, in a project/ application/module/action structure. But be aware that the actual file tree structure of the project will differ from the setup shown in the figure.

Figure 2-3 - Example of code organization

Example of code organization
File Tree Structure

All web projects generally share the same types of contents, such as the following:

    * A database, such as MySQL or PostgreSQL
    * Static files (HTML, images, JavaScript files, style sheets, and so on)
    * Files uploaded by the site users and administrators
    * PHP classes and libraries
    * Foreign libraries (third-party scripts)
    * Batch files (scripts to be launched by a command line or via a cron table)
    * Log files (traces written by the application and/or the server)
    * Configuration files

Symfony provides a standard file tree structure to organize all these contents in a logical way, consistent with the architecture choices (MVC pattern and project/application/module grouping). This is the tree structure that is automatically created when initializing every project, application, or module. Of course, you can customize it completely, to reorganize the files and directories at your convenience or to match your client's requirements.
Root Tree Structure

These are the directories found at the root of a symfony project:

apps/
  frontend/
  backend/
batch/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

Table 2-1 describes the contents of these directories.

Table 2-1 - Root Directories
Directory     Description
apps/     Contains one directory for each application of the project (typically, frontend and backend for the front and back office).
batch/     Contains PHP scripts called from a command line or a scheduler, to run batch processes.
cache/     Contains the cached version of the configuration, and (if you activate it) the cache version of the actions and templates of the project. The cache mechanism (detailed in Chapter 12) uses these files to speed up the answer to web requests. Each application will have a subdirectory here, containing preprocessed PHP and HTML files.
config/     Holds the general configuration of the project.
data/     Here, you can store the data files of the project, like a database schema, a SQL file that creates tables, or even a SQLite database file.
doc/     Stores the project documentation, including your own documents and the documentation generated by PHPdoc.
lib/     Dedicated to foreign classes or libraries. Here, you can add the code that needs to be shared among your applications. The model/ subdirectory stores the object model of the project (described in Chapter 8).
log/     Stores the applicable log files generated directly by symfony. It can also contain web server log files, database log files, or log files from any part of the project. Symfony creates one log file per application and per environment (log files are discussed in Chapter 16).
plugins/     Stores the plug-ins installed in the application (plug-ins are discussed in Chapter 17).
test/     Contains unit and functional tests written in PHP and compatible with the symfony testing framework (discussed in Chapter 15). During the project setup, symfony automatically adds some stubs with a few basic tests.
web/     The root for the web server. The only files accessible from the Internet are the ones located in this directory.
Application Tree Structure

The tree structure of all application directories is the same:

apps/
  [application name]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
      error.php
      error.txt

Table 2-2 describes the application subdirectories.

Table 2-2 - Application Subdirectories
Directory     Description
config/     Holds a hefty set of YAML configuration files. This is where most of the application configuration is, apart from the default parameters that can be found in the framework itself. Note that the default parameters can still be overridden here if needed. You'll learn more about application configuration in the Chapter 5.
i18n/     Contains files used for the internationalization of the application--mostly interface translation files (Chapter 13 deals with internationalization). You can bypass this directory if you choose to use a database for internationalization.
lib/     Contains classes and libraries that are specific to the application.
modules/     Stores all the modules that contain the features of the application.
templates/     Lists the global templates of the application--the ones that are shared by all modules. By default, it contains a layout.php file, which is the main layout in which the module templates are inserted.

    The i18n/, lib/, and modules/ directories are empty for a new application.

The classes of an application are not able to access methods or attributes in other applications of the same project. Also note that hyperlinks between two applications of the same project must be in absolute form. You need to keep this last constraint in mind during initialization, when you choose how to divide your project into applications.
Module Tree Structure

Each application contains one or more modules. Each module has its own subdirectory in the modules directory, and the name of this directory is chosen during the setup.

This is the typical tree structure of a module:

apps/
  [application name]/
    modules/
      [module name]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

Table 2-3 describes the module subdirectories.

Table 2-3 - Module Subdirectories
Directory     Description
actions/     Generally contains a single class file named actions.class.php, in which you can store all the actions of the module. You can also write different actions of a module in separate files.
config/     Can contain custom configuration files with local parameters for the module.
lib/     Stores classes and libraries specific to the module.
templates/     Contains the templates corresponding to the actions of the module. A default template, called indexSuccess.php, is created during module setup.
validate/     Dedicated to configuration files used for form validation (discussed in Chapter 10).

    The config/, lib/, and validate/ directories are empty for a new module.

Web Tree Structure

There are very few constraints for the web directory, which is the directory of publicly accessible files. Following a few basic naming conventions will provide default behaviors and useful shortcuts in the templates. Here is an example of a web directory structure:

web/
  css/
  images/
  js/
  uploads/

Conventionally, the static files are distributed in the directories listed in Table 2-4.

Table 2-4 - Typical Web Subdirectories
Directory     Description
css/     Contains style sheets with a .css extension.
images/     Contains images with a .jpg, .png, or .gif format.
js/     Holds JavaScript files with a .js extension.
uploads/     Must contain the files uploaded by the users. Even though the directory usually contains images, it is distinct from the images directory so that the synchronization of the development and production servers does not affect the uploaded images.

    Even though it is highly recommended that you maintain the default tree structure, it is possible to modify it for specific needs, such as to allow a project to run in a server with different tree structure rules and coding conventions. Refer to Chapter 19 for more information about modifying the file tree structure.

Common Instruments

A few techniques are used repeatedly in symfony, and you will meet them quite often in this book and in your own projects. These include parameter holders, constants, and class autoloading.
Parameter Holders

Many of the symfony classes contain a parameter holder. It is a convenient way to encapsulate attributes with clean getter and setter methods. For instance, the sfResponse class holds a parameter holder that you can retrieve by calling the getParameterHolder() method. Each parameter holder stores data the same way, as illustrated in Listing 2-15.

Listing 2-15 - Using the sfResponse Parameter Holder

$response->getParameterHolder()->set('foo', 'bar');
echo $response->getParameterHolder()->get('foo');
 => 'bar'

Most of the classes using a parameter holder provide proxy methods to shorten the code needed for get/set operations. This is the case for the sfResponse object, so you can do the same as in Listing 2-15 with the code of Listing 2-16.

Listing 2-16 - Using the sfResponse Parameter Holder Proxy Methods

$response->setParameter('foo', 'bar');
echo $response->getParameter('foo');
 => 'bar'

The parameter holder getter accepts a default value as a second argument. This provides a useful fallback mechanism that is much more concise than possible with a conditional statement. See Listing 2-17 for an example.

Listing 2-17 - Using the Attribute Holder Getter's Default Value

// The 'foobar' parameter is not defined, so the getter returns an empty value
echo $response->getParameter('foobar');
 => null
 
// A default value can be used by putting the getter in a condition
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default
 
// But it is much faster to use the second getter argument for that
echo $response->getParameter('foobar', 'default');
 => default

The parameter holders even support namespaces. If you specify a third argument to a setter or a getter, it is used as a namespace, and the parameter will be defined only within that namespace. Listing 2-18 shows an example.

Listing 2-18 - Using the sfResponse Parameter Holder Namespace

$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
 => 'bar2'

Of course, you can add a parameter holder to your own classes to take advantage of its syntax facilities. Listing 2-19 shows how to define a class with a parameter holder.

Listing 2-19 - Adding a Parameter Holder to a Class

class MyClass
{
  protected $parameter_holder = null;
 
  public function initialize ($parameters = array())
  {
    $this->parameter_holder = new sfParameterHolder();
    $this->parameter_holder->add($parameters);
  }
 
  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
}

Constants

You will not find any constants in symfony because by their very nature you can't change their value once they are defined. Symfony uses its own configuration object, called sfConfig, which replaces constants. It provides static methods to access parameters from everywhere. Listing 2-19 demonstrates the use of sfConfig class methods.

Listing 2-20 - Using the sfConfig Class Methods Instead of Constants

// Instead of PHP constants,
define('SF_FOO', 'bar');
echo SF_FOO;
// Symfony uses the sfConfig object
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

The sfConfig methods support default values, and you can call the sfConfig::set() method more than once on the same parameter to change its value. Chapter 5 discusses sfConfig methods in more detail.
Class Autoloading

Usually, when you use a class method or create an object in PHP, you need to include the class definition first:

include 'classes/MyClass.php';
$myObject = new MyClass();

On large projects with many classes and a deep directory structure, keeping track of all the class files to include and their paths can be time consuming. By providing an spl_autoload_register() function, symfony makes include statements unnecessary, and you can write directly:

$myObject = new MyClass();

Symfony will then look for a MyClass definition in all files ending with php in one of the project's lib/ directories. If the class definition is found, it will be included automatically.

So if you store all your classes in lib/ directories, you don't need to include classes anymore. That's why the symfony projects usually do not contain any include or require statements.

    For better performance, the symfony autoloading scans a list of directories (defined in an internal configuration file) during the first request. It then registers all the classes these directories contain and stores the class/file correspondence in a PHP file as an associative array. That way, future requests don't need to do the directory scan anymore. This is why you need to clear the cache every time you add or move a class file in your project by calling the symfony clear-cache command. You will learn more about the cache in Chapter 12, and about the autoloading configuration in Chapter 19.

Summary

Using an MVC framework forces you to divide and organize your code according to the framework conventions. Presentation code goes to the view, data manipulation code goes to the model, and the request manipulation logic goes to the controller. It makes the application of the MVC pattern both very helpful and quite restricting.

Symfony is an MVC framework written in PHP 5. Its structure is designed to get the best of the MVC pattern, but with great ease of use. Thanks to its versatility and configurability, symfony is suitable for all web application projects.

Now that you understand the underlying theory behind symfony, you are almost ready to develop your first application. But before that, you need a symfony installation up and running on your development server.
 

As you've learned in previous chapters, the symfony framework is a set of files written in PHP. A symfony project uses these files, so installing symfony means getting these files and making them available for the project.

Being a PHP 5 framework, symfony requires PHP 5. Make sure you have it installed by opening a command line and typing this command:

> php -v

PHP 5.2.0 (cli) (built: Nov 2 2006 11:57:36)
Copyright (c) 1997-2006 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies

If the version number is 5.0 or higher, then you're ready for the installation, as described in this chapter.
Installing the Sandbox

If you just want to see what symfony is capable of, you'll probably go for the fast installation. In that case, you need the sandbox.

The sandbox is a simple archive of files. It contains an empty symfony project including all the required libraries (symfony, pake, lime, Creole, Propel, and Phing), a default application, and basic configuration. It will work out of the box, without specific server configuration or any additional packages.

To install it, download the sandbox archive from http://www.symfony-project.org/get/sf_sandbox.tgz. Unpack it under the root web directory configured for your server (usually web/ or www/). For the purposes of uniformity, this chapter will assume you unpacked it to the directory sf_sandbox/.

    Having all the files under the root web directory is fine for your own tests in a local host, but is a bad practice in a production server. It makes all the internals of your application visible to end users.

Test your installation by executing the symfony CLI. Go to the new sf_sandbox/ directory and type the following on a *nix system:

> ./symfony -V

On Windows, issue this command:

> symfony -V

You should see the sandbox version number:

symfony version 1.0.0

Now make sure that your web server can browse the sandbox by requesting this URL:

http://localhost/sf_sandbox/web/frontend_dev.php/

You should see a congratulations page that looks like Figure 3-1, and it means that your installation is finished. If not, then an error message will guide you through the configuration changes needed. You can also refer to the "Troubleshooting" section later in this chapter.

Figure 3-1 - Sandbox congratulations page

Sandbox congratulations page

The sandbox is intended for you to practice with symfony on a local computer, not to develop complex applications that may end up on the Web. However, the version of symfony shipped with the sandbox is fully functional and equivalent to the one you can install via PEAR.

To uninstall a sandbox, just remove the sf_sandbox/ directory from your web/ folder.
Installing the Symfony Libraries

When developing an application, you will probably need to install symfony twice: once for your development environment and once for the host server (unless your host already has symfony installed). For each server, you will probably want to avoid duplication by keeping all the symfony files in a single place, whether you develop only one application or several applications.

Since the symfony framework evolves quickly, a new stable version could very well be released only a few days after your first installation. You need to think of the framework upgrade as a major concern, and that's another reason why you should share one instance of the symfony libraries across all your symfony projects.

When it comes to installing the libraries for a real application development, you have two alternatives:

    * The PEAR installation is recommended for most people. It can be easily shared and upgraded, and the installation process is straightforward.
    * The Subversion (SVN) installation is meant to be used only by advanced PHP developers, who want to take advantage of the latest patches, add features of their own, and/or contribute to the symfony project.

Symfony integrates a few other packages:

    * pake is a CLI utility.
    * lime is a unit testing utility.
    * Creole is a database abstraction engine. Just like PHP Data Objects (PDO), it provides an interface between your code and the database SQL code, and makes it possible to switch to another engine.
    * Propel is for ORM. It provides object persistence and query service.
    * Phing is a build system used by Propel to generate the model classes.

Pake and lime are developed by the symfony team. Creole, Propel, and Phing come from another team and are released under the GNU Lesser Public General License (LGPL). All these packages are bundled with symfony.
Installing the Symfony PEAR Package

The symfony PEAR package contains the symfony libraries and all its dependencies. It also contains a script that will extend your CLI to include the symfony command.

The first step to install it is to add the symfony channel to PEAR, by issuing this command:

> pear channel-discover pear.symfony-project.com

To see the libraries available in this channel, type the following:

> pear remote-list -c symfony

Now you are ready to install the latest stable version of symfony. Issue this command:

> pear install symfony/symfony

downloading symfony-1.0.0.tgz ...
Starting to download symfony-1.0.0.tgz (1,283,270 bytes)
.................................................................
.................................................................
.............done: 1,283,270 bytes
install ok: channel://pear.symfony-project.com/symfony-1.0.0

That's it. The symfony files and CLI are installed. Check that the installation succeeded by calling the new symfony command line, asking for the version number:

> symfony -V

symfony version 1.0.0

    If you prefer to install the most recent beta, which has the latest bug fixes and enhancements, type pear install symfony/symfony-beta instead. Beta releases are not completely stable and are generally not recommended for production environments.

The symfony libraries are now installed in directories as follows:

    * $php_dir/symfony/ contains the main libraries.
    * $data_dir/symfony/ contains the skeleton of symfony applications; default modules; and configuration, i18n data, and so on.
    * $doc_dir/symfony/ contains the documentation.
    * $test_dir/symfony/ contains unit tests.

The _dir variables are part of your PEAR configuration. To see their values, type the following:

> pear config-show

Checking Out Symfony from the SVN Repository

For production servers, or when PEAR is not an option, you can download the latest version of the symfony libraries directly from the symfony Subversion repository by requesting a checkout:

> mkdir /path/to/symfony
> cd /path/to/symfony
> svn checkout http://svn.symfony-project.com/tags/RELEASE_1_0_0/ .

    For the latest stable bug-fix release on the 1.0 branch (1.0.x) refer to (http://www.symfony-project.org/installation/1_0)

The symfony command, available only for PEAR installations, is a call to the /path/to/symfony/data/bin/symfony script. So the following would be the equivalent to the symfony -V command for an SVN installation:

> php /path/to/symfony/data/bin/symfony -V

symfony version 1.0.0

If you chose an SVN installation, you probably already have an existing symfony project. For this project to make use of the symfony files, you need to change the two variables defined in your project's config/config.php file, as follows:

<?php
 
$sf_symfony_lib_dir  = '/path/to/symfony/lib';
$sf_symfony_data_dir = '/path/to/symfony/data';

Chapter 19 proposes other ways to link a project with a symfony installation (including symbolic links and relative paths).

    Alternatively, you can also download the PEAR package. Refer to
    (http://www.symfony-project.org/installation/1_0) for latest 1.0 release. You will have the same result as with a checkout.

Setting Up an Application

As you learned in Chapter 2, symfony gathers related applications in projects. All the applications of a project share the same databases. In order to set up an application, you must first set up a project.
Creating the Project

Each symfony project follows a predefined directory structure. The symfony command line automates the creation of new projects by initiating the skeleton of the project, with the proper tree structure and access rights. So to create a project, simply create a new directory and ask symfony to make it a project.

For a PEAR installation, issue these commands:

> mkdir ~/myproject
> cd ~/myproject
> symfony init-project myproject

For an SVN installation, create a project with these commands:

> mkdir ~/myproject
> cd ~/myproject
> php /path/to/symfony/data/bin/symfony init-project myproject

The symfony command must always be called from the project's root directory (myproject/ in the preceding examples), because all the tasks performed by this command are project-specific.

Symfony will create a directory structure that looks like this:

apps/
batch/
cache/
config/
data/
doc/
lib/
log/
plugins/
test/
web/

    The init-project task adds a symfony script in the project root directory. This PHP script does the same as the symfony command installed by PEAR, so you can call php symfony instead of symfony if you don't have native command-line support (for SVN installations).

Creating the Application

The project is not yet ready to be viewed, because it requires at least one application. To initialize it, use the symfony init-app command and pass the name of the application as an argument:

> symfony init-app myapp

This will create a myapp/ directory in the apps/ folder of the project root, with a default application configuration and a set of directories ready to host the file of your website:

apps/
  myapp/
    config/
    i18n/
    lib/
    modules/
    templates/

Some PHP files corresponding to the front controllers of each default environment are also created in the project web directory:

web/
  index.php
  myapp_dev.php

index.php is the production front controller of the new application. Because you created the first application of the project, symfony created a file called index.php instead of myapp.php (if you now add a new application called mynewapp, the new production front controller will be named mynewapp.php). To run your application in the development environment, call the front controller myapp_dev.php. You'll learn more about these environments in Chapter 5.
Configuring the Web Server

The scripts of the web/ directory are the entry points to the application. To be able to access them from the Internet, the web server must be configured. In your development server, as well as in a professional hosting solution, you probably have access to the Apache configuration and you can set up a virtual host. On a shared-host server, you probably have access only to an .htaccess file.
Setting Up a Virtual Host

Listing 3-1 is an example of Apache configuration, where a new virtual host is added in the httpd.conf file.

Listing 3-1 - Sample Apache Configuration, in apache/conf/httpd.conf

<VirtualHost *:80>
  ServerName myapp.example.com
  DocumentRoot "/home/steve/myproject/web"
  DirectoryIndex index.php
  Alias /sf /$sf_symfony_data_dir/web/sf
  <Directory "/$sf_symfony_data_dir/web/sf">
    AllowOverride All
    Allow from All
  </Directory>
  <Directory "/home/steve/myproject/web">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>

In the configuration in Listing 3-1, the $sf_symfony_data_dir placeholder must be replaced by the actual path. For example, for a PEAR installation in *nix, you should type something like this:

    Alias /sf /usr/local/lib/php/data/symfony/web/sf

    The alias to the web/sf/ directory is not mandatory. It allows Apache to find images, style sheets, and JavaScript files for the web debug toolbar, the admin generator, the default symfony pages, and the Ajax support. An alternative to this alias would be to create a symbolic link (symlink) or copy the /path/to/symfony/data/web/sf/ directory to myproject/web/sf/.

    TIP If you have installed symfony via PEAR and can't find the symfony shared data directory, look in the PEAR data_dir which is listed in the PEAR config:

    pear config-show

Restart Apache, and that's it. Your newly created application can now be called and viewed through a standard web browser at the following URL:

http://localhost/myapp_dev.php/

You should see a congratulations page similar to the one shown earlier in Figure 3-1.

    URL Rewriting

    Symfony uses URL rewriting to display "smart URLs"--meaningful locations that display well on search engines and hide all the technical data from the user. You will learn more about this feature, called routing, in Chapter 9.

    If your version of Apache is not compiled with the mod_rewrite module, check that you have the mod_rewrite Dynamic Shared Object (DSO) installed and the following lines in your httpd.conf:

    AddModule mod_rewrite.c LoadModule rewrite_module modules/mod_rewrite.so

    For Internet Information Services (IIS), you will need isapi/rewrite installed and running. Check the symfony online documentation for a detailed IIS installation guide.

Configuring a Shared-Host Server

Setting up an application in a shared host is a little trickier, since the host usually has a specific directory layout that you can't change.

    Doing tests and development directly in a shared host is not a good practice. One reason is that it makes the application visible even if it is not finished, revealing its internals and opening large security breaches. Another reason is that the performance of shared hosts is often not sufficient to browse your application with the debug tools on efficiently. So you should not start your development with a shared-host installation, but rather build your application locally and deploy it to the shared host when it is finished. Chapter 16 will tell you more about deployment techniques and tools.

Let's imagine that your shared host requires that the web folder is named www/ instead of web/, and that it doesn't give you access to the httpd.conf file, but only to an .htaccess file in the web folder.

In a symfony project, every path to a directory is configurable. Chapter 19 will tell you more about it, but in the meantime, you can still rename the web directory to www and have the application take it into account by changing the configuration, as shown in Listing 3-2. These lines are to be added to the end of the application config.php file.

Listing 3-2 - Changing the Default Directory Structure Settings, in apps/myapp/config/config.php

$sf_root_dir = sfConfig::get('sf_root_dir');
sfConfig::add(array(
  'sf_web_dir_name' => $sf_web_dir_name = 'www',
  'sf_web_dir'      => $sf_root_dir.DIRECTORY_SEPARATOR.$sf_web_dir_name,
  'sf_upload_dir'   => $sf_root_dir.DIRECTORY_SEPARATOR.$sf_web_dir_name.DIRECTORY_SEPARATOR.sfConfig::get('sf_upload_dir_name'),
));

The project web root contains an .htaccess file by default. It is shown in Listing 3-3. Modify it as necessary to match your shared host requirements.

Listing 3-3 - Default .htaccess Configuration, Now in myproject/www/.htaccess

Options +FollowSymLinks +ExecCGI

<IfModule mod_rewrite.c>
  RewriteEngine On

  # we skip all files with .something
  RewriteCond %{REQUEST_URI} \..+$
  RewriteCond %{REQUEST_URI} !\.html$
  RewriteRule .* - [L]

  # we check if the .html version is here (caching)
  RewriteRule ^$ index.html [QSA]
  RewriteRule ^([^.]+)$ $1.html [QSA]
  RewriteCond %{REQUEST_FILENAME} !-f

  # no, so we redirect to our front web controller
  RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

# big crash from our front web controller
ErrorDocument 500 "<h2>Application error</h2>symfony applicationfailed to start properly"

You should now be ready to browse your application. Check the congratulation page by requesting this URL:

http://www.example.com/myapp_dev.php/

    Other Server Configurations

    Symfony is compatible with other server configurations. You can, for instance, access a symfony application using an alias instead of a virtual host. You can also run a symfony application with an IIS server. There are as many techniques as there are configurations, and it is not the purpose of this book to explain them all.

    To find directions for a specific server configuration, refer to the symfony wiki (http://trac.symfony-project.org/), which contains many step-by-step tutorials.

Troubleshooting

If you encounter problems during the installation, try to make the best out of the errors or exceptions thrown to the shell or to the browser. They are often self-explanatory and may even contain links to specific resources on the Web about your issue.
Typical Problems

If you are still having problems getting symfony running, check the following:

    *

      Some PHP installations come with both a PHP 4 and a PHP 5 command. In that case, the command line is probably php5 instead of php, so try calling php5 symfony instead of the symfony command. You may also need to add SetEnv PHP_VER 5 to your .htaccess configuration, or rename the scripts of the web/ directory from .php to .php5. The error thrown by a PHP 4 command line trying to access symfony looks like this:

      Parse error, unexpected ',', expecting '(' in .../symfony.php on line 19.

    *

      The memory limit, defined in the php.ini, must be set to 16M at least. The usual symptom for this problem is an error message when installing symfony via PEAR or using the command line.

      Allowed memory size of 8388608 bytes exhausted

    *

      The zend.ze1_compatibility_mode setting must be set to off in your php.ini. If it is not, trying to browse to one of the web scripts will produce an "implicit cloning" error:

      Strict Standards: Implicit cloning object of class 'sfTimer'because of 'zend.ze1_compatibility_mode'

    *

      The log/ and cache/ directories of your project must be writable by the web server. Attempts to browse a symfony application without these directory permissions will result in an exception:

      sfCacheException [message] Unable to write cache file"/usr/myproject/cache/frontend/prod/config/config_config_handlers.yml.php"

    *

      The include path of your system must include the path to the php command, and the include path of your php.ini must contain a path to PEAR (if you use PEAR).
    * Sometimes, there is more than one php.ini on a server's file system (for instance, if you use the WAMP package). Call phpinfo() to know the exact location of the php.ini file used by your application.

    Although it is not mandatory, it is strongly advised, for performance reasons, to set the magic_quotes_gpc and register_globals settings to off in your php.ini.

Symfony Resources

You can check if your problem has already happened to someone else and find solutions in various places:

    * The symfony installation forum (http://www.symfony-project.org/forum/) is full of installation questions about a given platform, environment, configuration, host, and so on.
    * The archives of the users mailing-list (http://groups.google.fr/group/symfony-users) are also searchable. You may find similar experiences to your own there.
    * The symfony wiki (http://trac.symfony-project.org/#Installingsymfony) contains step-by-step tutorials, contributed by symfony users, about installation.

If you don't find any answer, try posing your question to the symfony community. You can post your query in the forum, the mailing list, or even drop to the #symfony IRC channel to get feedback from the most active members of the community.
Source Versioning

Once the setup of the application is done, starting a source versioning (or version control) process is recommended. Source versioning keeps track of all modifications in the code, gives access to previous releases, facilitates patching, and allows for efficient team work. Symfony natively supports CVS, although Subversion (http://subversion.tigris.org/) is recommended. The following examples show the commands for Subversion, and assume that you already have a Subversion server installed and that you wish to create a new repository for your project. For Windows users, a recommended Subversion client is TortoiseSVN (http://tortoisesvn.tigris.org/). For more information about source versioning and the commands used here, consult the Subversion documentation.

The following example assumes that $SVNREP_DIR is defined as an environment variable. If you don't have it defined, you will need to substitute the actual location of the repository in place of $SVNREP_DIR.

So let's create the new repository for the myproject project:

> svnadmin create $SVNREP_DIR/myproject

Then the base structure (layout) of the repository is created with the trunk, tags, and branches directories with this pretty long command:

> svn mkdir -m "layout creation" file:///$SVNREP_DIR/myproject/trunk file:///$SVNREP_DIR/myproject/tags file:///$SVNREP_DIR/myproject/branches

This will be your first revision. Now you need to import the files of the project except the cache and log temporary files:

> cd ~/myproject
> rm -rf cache/*
> rm -rf log/*
> svn import -m "initial import" . file:///$SVNREP_DIR/myproject/trunk

Check the committed files by typing the following:

> svn ls file:///$SVNREP_DIR/myproject/trunk/

That seems good. Now the SVN repository has the reference version (and the history) of all your project files. This means that the files of the actual ~/myproject/ directory need to refer to the repository. To do that, first rename the myproject/ directory--you will erase it soon if everything works well--and do a checkout of the repository in a new directory:

> cd ~
> mv myproject myproject.origin
> svn co file:///$SVNREP_DIR/myproject/trunk myproject
> ls myproject

That's it. Now you can work on the files located in ~/myproject/ and commit your modifications to the repository. Don't forget to do some cleanup and erase the myproject.origin/ directory, which is now useless.

There is one remaining thing to set up. If you commit your working directory to the repository, you may copy some unwanted files, like the ones located in the cache and log directories of your project. So you need to specify an ignore list to SVN for this project. You also need to set full access to the cache/ and log/ directories again:

> cd ~/myproject
> chmod 777 cache
> chmod 777 log
> svn propedit svn:ignore log
> svn propedit svn:ignore cache

The default text editor configured for SVN should launch. If this doesn't happen, make Subversion use your preferred editor by typing this:

> export SVN_EDITOR=<name of editor>
> svn propedit svn:ignore log
> svn propedit svn:ignore cache

Now simply add all files from the subdirectories of myproject/ that SVN should ignore when committing:

*

Save and quit. You're finished.
Summary

To test and play with symfony on your local server, your best option for installation is definitely the sandbox, which contains a preconfigured symfony environment.

For a real development or in a production server, opt for the PEAR installation or the SVN checkout. This will install the symfony libraries, and you still need to initialize a project and an application. The last step of the application setup is the server configuration, which can be done in many ways. Symfony works perfectly fine with a virtual host, and it is the recommended solution.

If you have any problems during installation, you will find many tutorials and answers to frequently asked questions on the symfony website. If necessary, you can submit your problem to the symfony community, and you will get a quick and effective answer.

Once your project is initiated, it is a good habit to start a version-control process.

Now that you are ready to use symfony, it is time to see how to build a basic web application.

Curiously, the first tutorial that programmers follow when learning a new language or a framework is the one that displays "Hello, world!" on the screen. It is strange to think of the computer as something that can greet the whole world, since every attempt in the artificial intelligence field has so far resulted in poor conversational abilities. But symfony isn't dumber than any other program, and the proof is, you can create a page that says "Hello, <Your Name Here>" with it.

This chapter will teach you how to create a module, which is a structural element that groups pages. You will also learn how to create a page, which is divided into an action and a template, because of the MVC pattern. Links and forms are the basic web interactions; you will see how to insert them in a template and handle them in an action.
Creating a Module Skeleton

As Chapter 2 explained, symfony groups pages into modules. Before creating a page, you need to create a module, which is initially an empty shell with a file structure that symfony can recognize.

The symfony command line automates the creation of modules. You just need to call the init-module task with the application name and the module name as arguments. In the previous chapter, you created a myapp application. To add a mymodule module to this application, type the following commands:

> cd ~/myproject
> symfony init-module myapp mymodule

>> dir+      ~/myproject/apps/myapp/modules/mymodule
>> dir+      ~/myproject/apps/myapp/modules/mymodule/actions
>> file+     ~/myproject/apps/myapp/modules/mymodule/actions/actions.class.php
>> dir+      ~/myproject/apps/myapp/modules/mymodule/config
>> dir+      ~/myproject/apps/myapp/modules/mymodule/lib
>> dir+      ~/myproject/apps/myapp/modules/mymodule/templates
>> file+     ~/myproject/apps/myapp/modules/mymodule/templates/indexSuccess.php
>> dir+      ~/myproject/apps/myapp/modules/mymodule/validate
>> file+     ~/myproject/test/functional/myapp/mymoduleActionsTest.php
>> tokens    ~/myproject/test/functional/myapp/mymoduleActionsTest.php
>> tokens    ~/myproject/apps/myapp/modules/mymodule/actions/actions.class.php
>> tokens    ~/myproject/apps/myapp/modules/mymodule/templates/indexSuccess.php

Apart from the actions/, config/, lib/, templates/, and validate/ directories, this command created only three files. The one in the test/ folder concerns unit tests, and you don't need to bother with it until Chapter 15. The actions.class.php (shown in Listing 4-1) forwards to the default module congratulation page. The templates/indexSuccess.php file is empty.

Listing 4-1 - The Default Generated Action, in actions/actions.class.php

<?php
 
class mymoduleActions extends sfActions
{
  public function executeIndex()
  {
    $this->forward('default', 'module');
  }
}

    If you look at an actual actions.class.php file, you will find more than these few lines, including a lot of comments. This is because symfony recommends using PHP comments to document your project and prepares each class file to be compatible with the phpDocumentor tool (http://www.phpdoc.org/).

For each new module, symfony creates a default index action. It is composed of an action method called executeIndex and a template file called indexSuccess.php. The meanings of the execute prefix and Success suffix will be explained in Chapters 6 and 7, respectively. In the meantime, you can consider that this naming is a convention. You can see the corresponding page (reproduced in Figure 4-1) by browsing to the following URL:

http://localhost/myapp_dev.php/mymodule/index

The default index action will not be used in this chapter, so you can remove the executeIndex() method from the actions.class.php file, and delete the indexSuccess.php file from the templates/ directory.

    Symfony offers other ways to initiate a module than the command line. One of them is to create the directories and files yourself. In many cases, actions and templates of a module are meant to manipulate data of a given table. As the necessary code to create, retrieve, update, and delete records from a table is often the same, symfony provides a mechanism called scaffolding to generate this code for you. Refer to Chapter 14 for more information about this technique.

Figure 4-1 - The default generated index page

The default generated index page
Adding a Page

In symfony, the logic behind pages is stored in the action, and the presentation is in templates. Pages without logic (still) require an empty action.
Adding an Action

The "Hello, world!" page will be accessible through a myAction action. To create it, just add an executeMyAction method to the mymoduleActions class, as shown in Listing 4-2.

Listing 4-2 - Adding an Action Is Like Adding an Execute Method to the Action Class

<?php
 
class mymoduleActions extends sfActions
{
  public function executeMyAction()
  {
  }
}

The name of the action method is always execute``Xxx``(), where the second part of the name is the action name with the first letter capitalized.

Now, if you request the following URL:

http://localhost/myapp_dev.php/mymodule/myAction

symfony will complain that the myActionSuccess.php template is missing. That's normal; in symfony, a page is always made of an action and a template.

    URLs (not domain names) are case-sensitive, and so is symfony (even though the method names are case-insensitive in PHP). This means that if you add an executemyaction() method, or an executeMyaction(), and then you call myAction with the browser, symfony will return a 404 error.

    URLs are part of the response

    Symfony contains a routing system that allows you to have a complete separation between the actual action name and the form of the URL needed to call it. This allows for custom formatting of the URL as if it were part of the response. You are no longer limited by the file structure nor by the request parameters; the URL for an action can look like the phrase you want. For instance, the call to the index action of a module called article usually looks like this:

    http://localhost/myapp_dev.php/article/index?id=123

    This URL retrieves a given article from a database. In this example, it retrieves an article (with id=123) in the Europe section that specifically discusses finance in France. But the URL can be written in a completely different way with a simple change in the routing.yml configuration file:

    http://localhost/articles/europe/france/finance.html

    Not only is the resulting URL search engine-friendly, it is also significant for the user, who can then use the address bar as a pseudo command line to do custom queries, as in the following:

    http://localhost/articles/tagged/finance+france+euro

    Symfony knows how to parse and generate smart URLs for the user. The routing system automatically peels the request parameters from a smart URL and makes them available to the action. It also formats the hyperlinks included in the response so that they look "smart". You will learn more about this feature in Chapter 9.

    Overall, this means that the way you name the actions of your applications should not be influenced by the way the URL used to call them should look, but by the actions' functions in the application. An action name explains what the action actually does, and it is often a verb in the infinitive form (like show, list, edit, and so on). Action names can be made totally invisible to the end user, so don't hesitate to use explicit action names (like listByName or showWithComments). You will economize on code comments to explain your action function, plus the code will be much easier to read.

Adding a Template

The action expects a template to render itself. A template is a file located in the templates/ directory of a module, named by the action and the action termination. The default action termination is a "success," so the template file to be created for the myAction action is to be called myActionSuccess.php.

Templates are supposed to contain only presentational code, so keep as little PHP code in them as possible. As a matter of fact, a page displaying "Hello, world!" can have a template as simple as the one in Listing 4-3.

Listing 4-3 - The mymodule/templates/myActionSuccess.php Template

<p>Hello, world!</p>

If you need to execute some PHP code in the template, you should avoid using the usual PHP syntax, as shown in Listing 4-4. Instead, write your templates using the PHP alternative syntax, as shown in Listing 4-5, to keep the code understandable for non-PHP programmers. Not only will the final code be correctly indented, but it will also help you keep the complex PHP code in the action, because only control statements (if, foreach, while, and so on) have an alternative syntax.

Listing 4-4 - The Usual PHP Syntax, Good for Actions, But Bad for Templates

<p>Hello, world!</p>
<?php
 
if ($test)
{
  echo "<p>".time()."</p>";
}
 
?>

Listing 4-5 - The Alternative PHP Syntax, Good for Templates

<p>Hello, world!</p>
<?php if ($test): ?>
<p><?php echo time(); ?></p>
<?php endif; ?>

    A good rule of thumb to check if the template syntax is readable enough is that the file should not contain HTML code echoed by PHP or curly brackets. And most of the time, when opening a <?php, you will close it with ?> in the same line.

Passing Information from the Action to the Template

The job of the action is to do all the complicated calculation, data retrieval, and tests, and to set variables for the template to be echoed or tested. Symfony makes the attributes of the action class (accessed via $this->variableName in the action) directly accessible to the template in the global namespace (via $variableName). Listings 4-6 and 4-7 show how to pass information from the action to the template.

Listing 4-6 - Setting an Action Attribute in the Action to Make It Available to the Template

<?php
 
class mymoduleActions extends sfActions
{
  public function executeMyAction()
  {
    $today = getdate();
    $this->hour = $today['hours'];
  }
}

Listing 4-7 - The Template Has Direct Access to the Action Attributes

<p>Hello, world!</p>
<?php if ($hour >= 18): ?>
<p>Or should I say good evening? It is already <?php echo $hour ?>.</p>
<?php endif; ?>

    The template already has access to a few pieces of data without the need of any variable setup in the action. Every template can call methods of the $sf_context, $sf_request, $sf_params, and $sf_user objects. They contain data related to the current context, request, request parameters, and session. You will soon learn how to use them efficiently.

Gathering Information from the User with Forms

Forms are a good way to get information from the user. Writing form and form elements in HTML can sometimes be cumbersome, especially when you want to be XHTML-compliant. You could include form elements in symfony templates the usual way, as shown in Listing 4-8, but symfony provides helpers that make this task easier.

Listing 4-8 - Templates Can Include Usual HTML Code

<p>Hello, world!</p>
<?php if ($hour >= 18): ?>
<p>Or should I say good evening? It is already <?php echo $hour ?>.</p>
<?php endif; ?>
<form method="post" action="/myapp_dev.php/mymodule/anotherAction">
  <label for="name">What is your name?</label>
  <input type="text" name="name" id="name" value="" />
  <input type="submit" value="Ok" />
</form>

A helper is a PHP function defined by symfony that is meant to be used within templates. It outputs some HTML code and is faster to use than writing the actual HTML code by yourself. Using symfony helpers, you can have the same result as in Listing 4-8 with the code shown in Listing 4-9.

Listing 4-9 - It Is Faster and Easier to Use Helpers Than to Use HTML Tags

<p>Hello, world!</p>
<?php if ($hour >= 18): ?>
<p>Or should I say good evening? It is already <?php echo $hour ?>.</p>
<?php endif; ?>
<?php echo form_tag('mymodule/anotherAction') ?>
  <?php echo label_for('name', 'What is your name?') ?>
  <?php echo input_tag('name') ?>
  <?php echo submit_tag('Ok') ?>
</form>

    Helpers are here to help you

    If, in the example in Listing 4-9, you think the helper version is not really faster to write than the HTML one, consider this one:

    <?php
    >     $card_list = array(
    >       'VISA' => 'Visa',
    >       'MAST' => 'MasterCard',
    >       'AMEX' => 'American Express',
    >       'DISC' => 'Discover');
    >     echo select_tag('cc_type', options_for_select($card_list, 'AMEX'));
    >     ?>

    This outputs the following HTML:

    <select name="cc_type" id="cc_type">
      <option value="VISA">Visa</option>
      <option value="MAST">MasterCard</option>
      <option value="AMEX" selected="selected">American Express</option>
      <option value="DISC">Discover</option>
    </select>

    The benefit of helpers in templates is raw speed of coding, clarity of code, and concision. The only price to pay is the time to learn them, which will end when you finish this book, and the time to write , for which you should already have a shortcut in your favorite text editor. So you could not use the symfony helpers in templates and write HTML the way you always did, but this would be a great loss and much less fun.

Note that the use of the short opening tags (<?=, equivalent to <?php echo) is not recommended for professional web applications, since your production web server may be able to understand more than one scripting language and consequently get confused. Besides, the short opening tags do not work with the default PHP configuration and need server tweaking to be activated. Ultimately, when you have to deal with XML and validation, it falls short because <? has a special meaning in XML.

Form manipulation deserves a whole chapter of its own, since symfony provides many tools, mostly helpers, to make it easier. You will learn more about these helpers in Chapter 10.
Linking to Another Action

You already know that there is a total decoupling between an action name and the URL used to call it. So if you create a link to anotherAction in a template as in Listing 4-10, it will only work with the default routing. If you later decide to change the way the URLs look, then you will need to review all templates to change the hyperlinks.

Listing 4-10 - Hyperlinks, the Classic Way

<a href="/myapp_dev.php/mymodule/anotherAction?name=anonymous">
  I never say my name
</a>

To avoid this hassle, you should always use the link_to() helper to create hyperlinks to your application's actions. Listing 4-11 demonstrates the use of the hyperlink helper.

Listing 4-11 - The link_to() Helper

<p>Hello, world!</p>
<?php if ($hour >= 18): ?>
<p>Or should I say good evening? It is already <?php echo $hour ?>.</p>
<?php endif; ?>
<?php echo form_tag('mymodule/anotherAction') ?>
  <?php echo label_for('name', 'What is your name?') ?>
  <?php echo input_tag('name') ?>
  <?php echo submit_tag('Ok') ?>
  <?php echo link_to('I never say my name','mymodule/anotherAction?name=anonymous') ?>
</form>

The resulting HTML will be the same as previously, except that when you change your routing rules, all the templates will behave correctly and reformat the URLs accordingly.

The link_to() helper, like many other helpers, accepts another argument for special options and additional tag attributes. Listing 4-12 shows an example of an option argument and the resulting HTML. The option argument is either an associative array or a simple string showing key=value couples separated by blanks.

Listing 4-12 - Most Helpers Accept an Option Argument

// Option argument as an associative array
<?php echo link_to('I never say my name', 'mymodule/anotherAction?name=anonymous',
  array(
    'class'    => 'special_link',
    'confirm'  => 'Are you sure?',
    'absolute' => true
)) ?>
 
// Option argument as a string
<?php echo link_to('I never say my name', 'mymodule/anotherAction?name=anonymous',
  'class=special_link confirm=Are you sure? absolute=true') ?>
 
// Both calls output the same
 => <a class="special_link" onclick="return confirm('Are you sure?');"
    href="http://localhost/myapp_dev.php/mymodule/anotherAction/name/anonymous">
    I never say my name</a>

Whenever you use a symfony helper that outputs an HTML tag, you can insert additional tag attributes (like the class attribute in the example in Listing 4-12) in the option argument. You can even write these attributes in the "quick-and-dirty" HTML 4.0 way (without double quotes), and symfony will output them in nicely formatted XHTML. That's another reason why helpers are faster to write than HTML.

    Because it requires an additional parsing and transformation, the string syntax is a little slower than the array syntax.

Like the form helpers, the link helpers are numerous and have many options. Chapter 9 will describe them in detail.
Getting Information from the Request

Whether the user sends information via a form (usually in a POST request) or via the URL (GET request), you can retrieve the related data from the action with the getRequestParameter() method of the sfActions object. Listing 4-13 shows how, in anotherAction, you retrieve the value of the name parameter.

Listing 4-13 - Getting Data from the Request Parameter in the Action

<?php
 
class mymoduleActions extends sfActions
{
  ...
 
  public function executeAnotherAction()
  {
    $this->name = $this->getRequestParameter('name');
  }
}

If the data manipulation is simple, you don't even need to use the action to retrieve the request parameters. The template has access to an object called $sf_params, which offers a get() method to retrieve the request parameters, just like the getRequestParameter() in the action.

If executeAnotherAction() were empty, Listing 4-14 shows how the anotherActionSuccess.php template would retrieve the same name parameter.

Listing 4-14 - Getting Data from the Request Parameter Directly in the Template

<p>Hello, <?php echo $sf_params->get('name') ?>!</p>

    Why not use the $_POST, $_GET, or $_REQUEST variables instead? Because then your URLs will be formatted differently (as in http://localhost/articles/europe/france/finance.html, without ? nor =), the usual PHP variables won't work anymore, and only the routing system will be able to retrieve the request parameters. And you may want to add input filtering to prevent malicious code injection, which is only possible if you keep all request parameters in one clean parameter holder.

The $sf_params object is more powerful than just giving a getter equivalent to an array. For instance, if you only want to test the existence of a request parameter, you can simply use the $sf_params->has() method instead of testing the actual value with get(), as in Listing 4-15.

Listing 4-15 - Testing the Existence of a Request Parameter in the Template

<?php if ($sf_params->has('name')): ?>
  <p>Hello, <?php echo $sf_params->get('name') ?>!</p>
<?php else: ?>
  <p>Hello, John Doe!</p>
<?php endif; ?>

You may have already guessed that this can be written in a single line. As with most getter methods in symfony, both the getRequestParameter() method in the action and the $sf_params->get() method in the template (which, as a matter of fact, calls the same method on the same object) accept a second argument: the default value to be used if the request parameter is not present.

<p>Hello, <?php echo $sf_params->get('name', 'John Doe') ?>!</p>

Summary

In symfony, pages are composed of an action (a method in the actions/actions.class.php file prefixed with execute) and a template (a file in the templates/ directory, usually ending with Success.php). They are grouped in modules, according to their function in the application. Writing templates is facilitated by helpers, which are functions provided by symfony that return HTML code. And you need to think of the URL as a part of the response, which can be formatted as needed, so you should refrain from using any direct reference to the URL in action naming or request parameter retrieval.

Once you know these basic principles, you can already write a whole web application with symfony. But it would take you way too long, since almost every task you will have to achieve during the course of the application development is facilitated one way or another by some symfony feature . . . which is why the book doesn't stop now.