Developing an Application in Symfony 1.3 (Part 1)

Tim Bowler

September 2009

The milkshake shop

Our application will be designed for a fictitious milkshake shop. The functional requirements for our shop are:

  • Display of the menu, job vacancies, and locations; these details will be updated from the back office
  • Sign up to a newsletter, where users can submit their details
  • Search facility for the menu
  • Secure backend for staff to update the site
  • The site must be responsive
  • Option for the users to view our vacancies in three languages

Creating the skeleton folder structure

Symfony has many ways in which it can help the developer create applications with less efforts—one way is by using the Symfony tasks available on the Command Line Interface (CLI). These Symfony tasks do the following:

  • Generate the folder structure for your project, modules, applications, and tasks
  • Clear the generated cache and rotate log files
  • Create controllers to enable and disable an application and set permissions
  • Interact with the ORM layer to build models and forms
  • Interact with SQL to insert data into the database
  • Generate initial tests and execute them
  • Search for text in your templates that need i18N dictionaries and create them

If you'd like to see all the tasks that are available with Symfony, just type the following on your command line:

Let's begin with using one of the Symfony tasks to generate the file structure for our project. Our project will reside in the milkshake folder. In your terminal window, change your folder path to the path that will hold your project and create this milkshake folder. My folder will reside in workspace folder. Therefore, I would type this:

$mkdir ~/workspace/milkshake && cd ~/workspace/milkshake

Now that I have the project folder and have changed the path to within the milkshake folder, let's use a Symfony task to generate the project file structure. In your terminal window, type the following:

$/home/timmy/workspace/milkshake>symfony generate:project -orm=Propel milkshake

We can generate our project we can also specify which ORM we would like to use. Our application is going to use the Propel ORM, but you can also opt for Doctrine ORM. By default, Doctrine ORM is enabled.

After pressing the Enter key, the task goes into action. Now you will see output like the one in the following screenshot on your terminal window. This screenshot shows the folder structure being created:

Symfony 1.3 Web Application Development

Let's have a look at the top-level folders that have been created in our project folder:




This folder contains our frontend application and any other applications that we will create


If there are any batch scripts, they are placed here


This folder is the location for cached files and/or scaffolded modules


This folder holds all the main configuration files and the database schema definitions


This folder holds data files such as test or fixture data


All our documentation should be stored in this folder; this includes briefs, PhpDoc for API, Entity Relationship Diagrams, and so on


Our models and classes are located in this folder so that all applications can access them


This folder holds the development controllers log files and our Apache log


All installed plugins are located in this folder


All unit and functional tests should be placed in this folder; Symfony will create and place stubs in this folder when we create our modules


This is the web root folder that contains all web resources such as images, CSS, JavaScript, and so on

There are three folders that must be writable by the web server. These are the cache, log, and web/uploads/. If these are not writable to your web server, then the server itself will either produce warnings at startup or fail to start. The files permissions are usually set during the generation process, but sometimes this can fail. You can use the following task to set the permissions:
$/home/timmy/workspace/milkshake>symfony project:permissions

With the initial project skeleton built, next we must create an application. Symfony defines an application as operations that are grouped logically and that can run independent of one another. For example, many sites have a frontend and back office/admin area. The admin area is where a user logs in and updates areas on the frontend. Of course, our application will also provide this functionality. We will call these areas frontend and backend. Our first application is going to be the frontend. Again, let's use a Symfony task to generate the frontend application folder structure. Enter the following task in your terminal window:

$/home/timmy/workspace/milkshake>symfony generate:app frontend

Again, after executing this task, you will see the following in your terminal window:

Symfony 1.3 Web Application Development

Let's have a look at the top-level folders that have just been created in the apps folder:




This folder will have all configuration files for our application


This folder will contain all Internationalization files


This folder will hold all application-specific classes


All our modules will be built in this folder


The default layout template is created in this folder, and any other global templates that will be created will also be placed in this folder

These steps will generate our project and frontend application folder structure, and we can now view our project in a web browser. Open your web browser and go to http://milkshake/frontend_dev.php and you will see the following default Symfony page:

Symfony 1.3 Web Application Development

Now we can see the default project page along with the instructions on what to do next. The frontend_dev.php file is our index file while developing the frontend application, and adheres to the naming convention of <application>_<environment>.php. This controller is the development controller and is rather helpful while developing. Before we continue though, I urge you to also have a look at the web debug toolbar.

Creating our database schema

When you think of creating the database schema, what generally springs to mind is either SQL CREATE statements or using a client such as phpMyAdmin to create the schema. But in Symfony, we will create the schema in either YAML or XML. For our application, we will be using the XML for a few reasons:

  • XML schemas are arguably easier to read than YAML schemas
  • Susceptibility of YAML to whitespace errors is exaggerated in these larger configuration files, and sometimes there is fall over with very large databases

Our schema may appear as though it's only for the database, but it is also a part of the ORM and forms generation process. As you can imagine, the schema is important for the integrity of our application.

Our database will be fairly simple. We will need to hold data for the milkshakes, flavors, store locations, vacancies, and user sign-up details. For visual reference, the entity relationship diagram is shown as follows:

Symfony 1.3 Web Application Development

By default, Symfony only generates a schema.yml file that is located in the config folder. As we want to create our schema using XML, simply rename the existing schema.yml file to schema.xml. Once you have done that, copy in the following schema:

<?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noXsd="true" package="lib.model">

<table name="milkshakes" idMethod="native" phpName="Milkshake">
<column name="id" type="INTEGER" required="true" autoIncrement="true"
primaryKey="true" index="true" />
<column name="name" type="VARCHAR" size="100" required="true" index="true" />
<column name="URL_slug" type="VARCHAR" size="100" required="true" />
<column name="image_URL" type="VARCHAR" size="40" required="true" />
<column name="thumb_URL" type="VARCHAR" size="40" required="true" />
<column name="calories" type="FLOAT" required="true" />
<column name="views" type="INTEGER" default="0"/>
<column name="created_at" type="TIMESTAMP" required="true" />
<column name="updated_at" type="TIMESTAMP" required="true" />
<index name="milkshake_name_index">
<index-column name="name"/>

<table name="flavors" idMethod="native" phpName="Flavor">
<column name="id" type="INTEGER" required="true" autoIncrement="true"
primaryKey="true" />
<column name="name" type="VARCHAR" size="20" required="true" />
<column name="created_at" type="TIMESTAMP" required="true" />

<table name="milkshake_flavors" idMethod="native" phpName="MilkshakeFlavor">
<column name="id" type="INTEGER" required="true" autoIncrement="true"
primaryKey="true" />
<column name="milkshake_id" type="INTEGER" required="true" />
<column name="flavor_id" type="INTEGER" required="true" />
<foreign-key foreignTable="flavors" onDelete="CASCADE">
<reference local="flavor_id" foreign="id" />
<foreign-key foreignTable="milkshakes" onDelete="CASCADE">
<reference local="milkshake_id" foreign="id" />

<table name="store_locations" idMethod="native" phpName="StoreLocation">
<column name="id" type="INTEGER" required="true" autoIncrement="true"
primaryKey="true" />
<column name="address1" type="VARCHAR" size="100" required="true" />
<column name="address2" type="VARCHAR" size="100" required="true" />
<column name="address3" type="VARCHAR" size="50" required="true" />
<column name="postcode" type="VARCHAR" size="8" required="true" />
<column name="city" type="VARCHAR" size="50" required="true" />
<column name="country" type="VARCHAR" size="50" required="true" />
<column name="phone" type="VARCHAR" size="20" required="true" />
<column name="fax" type="VARCHAR" size="20" required="true" />

<table name="vacancies" idMethod="native" phpName="Vacancy">
<column name="id" type="INTEGER" required="true" autoIncrement="true"
primaryKey="true" />
<column name="position" type="VARCHAR" size="30" required="true" />
<column name="position_description" type="VARCHAR" size="100" required="true" />
<column name="location_id" type="INTEGER" required="true" />
<foreign-key foreignTable="store_locations" onDelete="CASCADE">
<reference local="location_id" foreign="id" />

When creating our schema, it is important to stick to the Propel and Symfony naming conventions—all table names should be plural and all models should be singular. The reason for this is that all the methods names are generated and have s appended to the function name. To retrieve all milkshake flavors, we would call the getMilkshakeFlavors() method from the milkshake object. If the object was in a plural form, the method created would be getMilkshakeFlavorss().

The schema itself is straightforward, but there are a few small options we should quickly go over:

  • package="lib.model"
    When Symfony will generate our models, they will be placed inside the lib/model folder. Later when we take a look at plugins, you will see that this can be changed.
  • onDelete="CASCADE"
    By default, Symfony enforces referential integrity by using the RESTRICT value for the onDelete attribute. This means that if we wanted to delete a milkshake, the system would prevent that as the milkshake is associated with flavors. Therefore, using the CASCADE value will allow us to delete the item.

    Further information about this is available on the Propel web site

  • phpName="MilkShake"
    Although this attribute can be omitted, it gives us the ability to name the generated models. Although Symfony will do this automatically, this adds flexibility.
  • required="true"
    The required attribute can be set to either true or false. This will either mean the field has to contain data or it doesn't have to. If this attribute is set to true when using the admin generator, the generated form will be in bold.

Symfony offers special handling of date/time fields. The fields must be named created_at or created_on, updated_at or updated_on. When first saving a record, if you have a field named created_at, Symfony will transparently add in the data/time. This also goes for the updated_at field.

We have created the database schema. Next, before using Symfony's CLI task, we must set up the database connections to build our database tables and the corresponding models, forms, and filters.

Configuring the ORM layer

The earlier versions of Symfony included the Propel ORM library as a part of its core components. But with releases of the new versions, it was decided to move the ORM out from its core framework and package the ORM as a plugin. The reason behind this is Doctrine, which is the other popular ORM used with Symfony. The new releases now allow the developer to easily choose which ORM they prefer. Both ORMs are packaged as plugins and are distributed with Symfony. Although Doctrine is now the default ORM, our application will be using Propel. However, there is a plugin that allows you to interchange ORMs.

As mentioned previously, the first file that Symfony reads is the ProjectConfiguration.class.php file, which is where we set configuration options for our project. It's located in our project's config folder. For example, we can enable plugins and set default folders. Now let's modify the ProjectConfiguration.class.php file from the config folder after making the change:

require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/

class ProjectConfiguration extends sfProjectConfiguration
public function setup()

Our configuration is what is included in the sandbox, if you have installed Symfony via PEAR then the path to the sfCoreAutolad.class.php will be different.

For us to enable Propel we have to change the plugin sfDoctrinePlugin to sfPropelPlugin like:

require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/

class ProjectConfiguration extends sfProjectConfiguration
public function setup()

Symfony uses autoloading to load all classes so that the developer does not need to use the require() function. The project configuration file is what starts this off, as you can see in the highlighted lines above. Also, you can see that our ORM plugin has been enabled. Every plugin that needs to be enabled needs to be defined here.

Configuring the database connection

In order to connect to our database, there are a few parameters that we need to set. These are the username, password, host, and the name of the database. All of these settings are defined in two configuration files—databases.yml and propel.ini—which again are located in the config folder with all other project-wide configuration files.

Setting these parameters can be done either by using a Symfony task on the CLI or by directly editing the files. Let's add them via the command line as follows:

$/home/timmy/workspace/milkshake>symfony configure:database
"mysql:host=localhost;dbname=milkshake" root myPassword

After executing the above task, open both databases.yml and propel.ini files. In them you will see that the database connection properties have now been updated.

Opening both of the database configuration files and entering the details manually is preferred because then you don't have to remember the tasks' parameters. Also, from a security point of view you should never enter a password directly on the command line where the password is in plain text.

Using another database engine

By default, Symfony is configured to use the MySQL database. Of course, Symfony supports many other databases such as PostgreSQL, Oracle, SQLite, and MSSQL. Just to show you how easy it is to change to another database, I will set up my project for use with PostgreSQL. This is included here only as an example to demonstrate how to change database engines. For our application, we will be using MySQL.

In the database.yml file, you will see the following:

class: sfPropelDatabase
classname: PropelPDO
dsn: 'pgsql:host=localhost;dbname=milkshake'
username: root
password: myPassword
encoding: utf8
persistent: true
pooling: true

In the propel.ini file, you will see the following:

propel.database = pgsql
propel.database.driver = pgsql
propel.database.url = pgsql:host=localhost;dbname=milkshake

Generating the models, forms, and filters

The Symfony tasks handle building all of the models, forms, and filters. When we were creating the database schema earlier, I mentioned that the schema is the root of all models, forms, and filters. To show this in action, we are going to generate them all. When you execute the tasks, the schema.xml file is parsed and then the ORM layer is generated based on it. At your terminal, execute the following three tasks:

$/home/timmy/workspace/milkshake>symfony propel:build-model
$/home/timmy/workspace/milkshake>symfony propel:build-forms
$/home/timmy/workspace/milkshake>symfony propel:build-filters

Now we have generated the entire ORM layer and all the generated classes are located in your lib folder. If you take a look inside the lib folder, you will see that there are three new subfolders—form, filter, and model—which all contain the classes.

In the following screenshot, you can see the overall folder structure:

Symfony 1.3 Web Application Development

>> Continue Reading: Developing an Application in Symfony 1.3 (Part 2)


If you have read this article you may be interested to view :

You've been reading an excerpt of:

Symfony 1.3 Web Application Development

Explore Title