As you probably know, Model-View-Controller (MVC) is a programming technique that Wikipedia defines as: "an architectural pattern used in software engineering. The pattern isolates 'domain logic' (the application logic for the user) from input and presentation (GUI), permitting independent development, testing and maintenance of each." If you apply this definition to PHP, you may very well come up with the Symfony framework. Symfony is an open-source PHP web framework that provides architecture, components and tools for developers to build and maintain complex web applications faster. The Symfony framework uses the MVC architecture to make the web applications a lot easier and faster to modify.
Symfony generates the front controller MVC logic component. The front controller and the layout are common to all actions in a Symfony-based application. You can use Symfony's Propel library to generate the classes of the model layer automatically. By providing a simple API for storing and retrieving data, the Propel library allows you to access your database using a set of objects. The database implementation is invisible to you. If you want to change the database, all you need do is modify a parameter. The view logic is a simple configuration file, which you can easily modify as you like.
Figure 1 shows the three components necessary for a single web page: the model layer, view layer, and controller layer.



Click here for larger image

Figure 1. The MVC Architecture

Create a Demo Application with Symfony

The application in this article is a sample Symfony project that demonstrates how you can use Symfony to develop professional applications with style and little effort. The demo project will be named bookshop, and it will represent an online bookstore. Because describing the entire application is beyond the scope of this article, it explains how to develop only a small part of it. But it includes enough to show what Symfony can do. To be more exact, the portion of the application demonstrated here is the mechanism for seeing the books, the function for deleting/editing/adding a book, and the view for searching for a book. As you will see, Symfony will do most of the work. Let's get started.

Install Latest Symfony Release

To install the Symfony 1.4 framework, you need a web server (Apache, for instance), a database engine (MySQL, PostgreSQL, SQLite, or any PDO-compatible database engine), and PHP 5.2.4 or later. Take the following steps to install Symfony 1.4:
  1. Download the latest stable version archive (1.4) to your computer.
  2. Unzip the archive in your favorite location (this example uses the {C:\demo\} folder), and rename it symfony.
  3. Navigate to the Symfony installation location, create the lib\vendor project directory, and move the unzipped archive symfony there.

Generating an Empty Symfony Project

To create a new project in Symfony, use the generate:project task. Open a MS-DOS command prompt (for Windows), navigate through the symfony folder path, and execute this command:
on Windows:
c:\demo> php lib\vendor\symfony\data\bin\symfony generate:project --orm=Propel bookshop

on Linux:
$ php lib\vendor\symfony\data\bin\symfony generate:project --orm=Propel bookshop
This task generates the structure of directories and files in Table 1 under /demo folder. These directories are all necessary for any Symfony project (this is an empty project).
Table 1. Required Directories for a Symfony Project
Directory Description
apps/ Hosts all project applications
cache/ The files cached by the framework
config/ The project configuration files
lib/ The project libraries and classes
log/ The framework log files
plugins/ The installed plugins
test/ The unit and functional test files
web/ The web root directory
Note: The generate:project task has created a symfony shortcut in the bookshop project root directory to simplify the path you have to write when running a task.
Note: Symfony can provide object-relational mapping by using Doctrine ORM or Propel ORM. By default, Symfony uses Doctrine, but you also may use Propel if you create the project by inserting the ┬ľorm=Propel option in the creation command.

Generate the Frontend Application

To generate the frontend application, you need to run the generate:app task like this:
on Windows
c:\demo> php symfony generate:app frontend

on Linux
$ php symfony generate:app frontend
Based on the application name passed as an argument, the generate:app task creates the default directory structure needed for the application under the apps/frontend directory (see Table 2).
Table 2. Default Directory Structure Needed for an Application
Directory Description
config/ The application configuration files
lib/ The application libraries and classes
modules/ The application code (MVC)
templates/ The global template files

Configure the Apache Server

The empty application is almost ready to run, but you need to change your Apache configuration first to make the new project accessible. For this, you must edit the httpd.conf file by adding in the following lines (at the end):
on Windows
# This is the configuration for your project
Listen 127.0.0.1:80

<VirtualHost 127.0.0.1:80>
  DocumentRoot "C:\demo\web"
  DirectoryIndex index.php
  <Directory "C:\demo\web">
    AllowOverride All
    Allow from All
  </Directory>

  Alias /sf "C:\demo\lib\vendor\symfony\data\web\sf"
  <Directory "C:\demo\lib\vendor\symfony\data\web\sf">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>

on Linux
# This is the configuration for your project
Listen 127.0.0.1:80

<VirtualHost 127.0.0.1:80>
  DocumentRoot "/home/sfprojects/demo/web"
  DirectoryIndex index.php
  <Directory "/home/sfprojects/demo/web">
    AllowOverride All
    Allow from All
  </Directory>

  Alias /sf "/home/sfprojects/demo/lib/vendor/symfony/data/web/sf"
  <Directory "/home/sfprojects/demo/lib/vendor/symfony/data/web/sf">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>
Now save the modifications, restart Apache, and access the http://localhost/index.php page. You should see something like Figure 2, a temporary page that states that your first Symfony project was created.



Click here for larger image

Figure 2. The First Created Project Using Symfony

Go further and access the application in the development environment by typing in the following URL:
http://localhost/frontend_dev.php/
The web debug toolbar should show on the top right corner, including small icons proving that your sf/ alias configuration is correct (see Figure 3).



Click here for larger image

Figure 3. The Page in Figure 2 Including the Web Debug Toolbar

The web debug toolbar is present on all pages in the development environment and you can click on its links to get more information in this stage. For example, by clicking the logs link you will obtain the logs for the current request (see Figure 4).



Click here for larger image

Figure 4. The Logs for Current Request

Note: At any given moment, a Symfony project is in one of the following environments:

Configure the Database for Bookshop

To develop the database for the bookshop application, start by creating an empty database under MySQL RDBMS named symfonydb. When you have done that, you then need to indicate to Symfony (and mandate to bookshop) to use this database. For this, you need to execute the configure:database task, like this:
On Windows:
c:\demo>php symfony configure:database 
"mysql:host=localhost;dbname=symfonydb"
root pass On Linux: $ php symfony configure:database
"mysql:host=localhost;dbname=symfonydb"
root pass
Note: The configure:database task takes three arguments: the PDO DSN, the username, and the password (optional) to access the database.
Note: The Symfony framework supports all PDO-supported databases, like MySQL, PostgreSQL, SQLite, Oracle, MSSQL, and so on.
Note: The configure:database task stores the database configuration into the /config/databases.yml configuration file. Instead of using the task, you can edit this file by hand.
At this point, Symfony knows which database to use, and you are ready to develop the relational model. Your database will contain a single table, named books, that has the following diagram:

The road from this diagram to the database is facilitated by the Symfony-supported ORMs (Propel or Doctrine). The ORM needs a description of the tables and their relationships to create the related classes. The two ways to create this description schema are introspecting an existing database and creating it by hand.
Obviously, in this case, you can do it by hand. Edit the empty config/schema.yml file, like this:
# config/schema.yml
propel:
  books:
    id:            ~
    title:         { type: varchar(255), required: true }
    author:        { type: varchar(255), required: true }
    publisher:     { type: varchar(255), required: true }
    price:         { type: varchar(255), required: true }
    yearofpub:     { type: varchar(255), required: false }
Now, based on the schema.yml, you can generate the proper SQL statements by running the propel:build-sql task, like this (the statements will be stored in the data/sql/ directory, optimized for the database engine you have configured, MySQL):
On Windows:
c:\demo>php symfony propel:build-sql

On Linux:
$ php symfony propel:build-sql
If you take a quick look in the data/sql/ folder, you will find the lib.model.schema.sql file with the following content:

# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;

#-----------------------------------------------------------------------------
#-- books
#-----------------------------------------------------------------------------

DROP TABLE IF EXISTS `books`;


CREATE TABLE `books`
(
   `id` INTEGER  NOT NULL AUTO_INCREMENT,
   `title` VARCHAR(255)  NOT NULL,
   `author` VARCHAR(255)  NOT NULL,
   `publisher` VARCHAR(255)  NOT NULL,
   `price` VARCHAR(255)  NOT NULL,
   `yearofpub` VARCHAR(255),
   PRIMARY KEY (`id`)
)Type=InnoDB;

# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;
Next, run these SQL statements against the database by running the propel:insert-sql task, like this:
On Windows:
c:\demo>php symfony propel:insert-sql

On Linux:
$ php symfony propel:insert-sql
The ORM also generates PHP classes that map table records to objects. You can accomplish this by running the propel:build-model task, like this (the generated PHP files are stored in lib/model/ folder):
On Windows:
c:\demo>php symfony propel:build-model

On Linux:
$ php symfony propel:build-model
The tables have been created in the database, but there is no data in them. Each time Symfony creates tables in the database, all the data are lost. To populate the database with some initial data, create YAML files in the data/fixtures/ directory and use the propel:data-load task to load them into the database.
First, create the following fixture file (save it as fixtures.yml):
# data/fixtures/fixtures.yml
books: 
 1: 
  title: SOA Patterns with BizTalk Server 2009
  author: Richard Seroter
  publisher: Packt
  price: E25.89
  yearofpub: 2009
 2: 
  title: Learning FreeNAS
  author: Gary Sims
  publisher: Packt
  price: E17.49
  yearofpub: 2008
 3: 
  title: MODx Web Development
  author: Antano Solar John
  publisher: Packt
  price: E17.49
  yearofpub: 2009
 4: 
  title: Apache Maven 2 Effective Implementation
  author: Brett Porter, Maria Odea Ching 
  publisher: Packt
  price: E17.49
  yearofpub: 2009
 5: 
  title: RESTful PHP Web Services
  author: Samisa Abeysinghe
  publisher: Packt
  price: E14.99
  yearofpub: 2008
Loading the initial data into the database is as simple as running the propel:data-load task:
On Windows:
c:\demo>php symfony propel:data-load

On Linux:
$ php symfony propel:data-load
Note: If you have decided to create the tables by writing SQL statements, you can generate the corresponding schema.yml configuration file by running the propel:build-schema task:
On Windows:
c:\demo>php symfony propel:build-schema

On Linux:
$ php symfony propel:build-schema
Note: You can compress all the tasks executed in this section by running a single one-named propel:build-all-load task, like this:
On Windows:
c:\demo>php symfony propel:build-all-load

On Linux:
$ php symfony propel:build-all-load

Generate the Web Pages

Now you have everything you need to create web pages that interact with the symfonydb database. The idea is to generate an application that displays a list of books, and allows the user to edit an existing book and delete a book. A Symfony project is composed of applications, and each application is further divided into modules. A module is a self-contained set of PHP code that represents a feature of the application, or a set of manipulations the user can do on a model object (a book module, for example). Symfony can automatically generate a module for a given model that provides basic manipulation features. To enable this feature, run the propel:generate-module task, like this (you generate the book module for the books model):
On Windows:
c:\demo>php symfony propel:generate-module --with-show --non-verbose-templates frontend book books

On Linux:
$ php php symfony propel:generate-module --with-show --non-verbose-templates frontend book books
Some files and directories have been created for you under the apps/frontend/modules/book/ directory (explore those files):
Directory Description
actions/ The module actions
templates/ The module templates
You can now test the book module in a browser at http://localhost/frontend_dev.php/book. The result should look like Figure 5 (navigate through the pages, and try to edit, delete and add a new book).



Click here for larger image

Figure 5. Testing the Book Module in the Browser

Customize Web Pages

To customize the generated web page, you first must know that in Symfony the content is rendered by a global template called a layout. The default layout of an application is called layout.php and you can find it in the apps/frontend/templates/ directory. This directory contains all the global templates for an application.
Replace the default Symfony layout with the following code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <?php include_http_metas() ?> <?php include_metas() ?> <?php include_title() ?> <link rel="shortcut icon" href="/favicon.ico" /> <?php include_stylesheets() ?> <?php include_javascripts() ?> </head> <body> <table border="0" width="100%"> <tr> <td> <img src="/images/books_banner.png" alt="header"/> </td> <form action="" method="get"> <td bgcolor="black"> <font color="white">Search:</font> <input size="45" type="text" > <input type="submit" value="Search" > <font type="arial" size="25" color="yellow">
<a href="/frontend_dev.php/books/new">New book</a></font> </td> </form> </tr> <tr> <td align="center" colspan="2"> <?php echo $sf_content ?> </td> </tr> <tr> <td colspan="2"> <img src="/images/footer.jpg" height="40" width="100%" alt="footer"/> </td> </tr> </table> </body> </html>
Note: The stylesheets, images, JavaScript and uploads are stored by default under the /web folder (in /css, /images, /js, and /uploads folders, respectively). Therefore, you should place your images under the /images folder.
Now, you can test the application again at http://localhost/frontend_dev.php/book. You should see something similar to Figure 6.



Click here for larger image

Figure 6. Testing the Book Module After Customizing the Web Page

Bookshop Complete

You have now completed the mechanism for seeing the books, the function for deleting/editing/adding a book, and the view for searching for a book. As you saw, Symfony did most of the work. Symfony is capable of so much more, but a full examination of the framework is well beyond the scope of this article. I hope that you have witnessed enough of Symfony's power to explore what else it can do at symfony-project.org.
About the Author
Octavia Andreea Anghel is a senior PHP developer currently working as a primary trainer for programming teams that participate at national and international software-development contests. She consults on developing educational projects at a national level. She is a coauthor of the book "XML Technologies--XML in Java" (Albastra, ISBN 978-973-650-210-1), for which she wrote the XML portions. In addition to PHP and XML, she's interested in software architecture, web services, UML, and high-performance unit tests. to e-mail her.