Development

sfBookPT-brMvcModel

You must first sign up to be able to contribute.

Version 10 (modified by hpoyatos, 11 years ago)
--

Symfony - Modelo (0.6.3)

Resumo

O Symfony possui um camada de abstração objeto/relacional baseada no projeto Propel. Atividades como acessar dados armazenados em um banco de dados e modificá-los são feitas de maneira fácil através de um objeto 'tradutor' para o banco de dados. Este capítulo explica a criação de um objeto data model (modelo de dados), e a forma de acessar e modificar informações com o Propel. Também ilustrará a integração do Propel com o Symfony.

Por que usar uma camada de abstração ?

Bancos de dados são relacionais. PHP5 e symfony são orientados à objeto. Para acessar um banco de dados de uma forma orientada à objetos, um interface tradutora de lógica de objetos para lógica relacional se faz necessária. Isso é chamado de mapeamento objeto-relacional, ou ORM.

Esta é a camada Model da arquitetura symfony MVC. It is made up of objects that give access to data and keep business rules within themselves.

Um benefício de uma camada de abstração objeto/relacional é que ela evita com que você use uma sintaxe especifica de um determinado tipo de banco de dados. Ela automaticamente traduz chamadas de objetos model para consultas SQL otimizadas para um determinado banco de dados.

Isso significa que trocar de um banco de dados para outro no meio do projeto é fácil. Imagine uma situação em que você precisa escrever um rápido protótipo de uma aplicação, mas o cliente ainda não se decidiu qual sistema de banco de dados seria o melhor para suas necessidade. Você pode iniciar construindo sua aplicação em SQLite, por exemplo, e trocar para um MySQL, PostgreSQL ou Oracle quando o cliente estivesse preparado para isso. Seria apenas trocar uma linha de um arquivo de configuração, e pronto !

Uma camada de abstração encapsula lógica de dados. O restante da aplicação não precisa conhecer as consultas SQL, e procurar uma SQL se torna uma tarefa fácil. Desenvolvedores que são especilizados em programação de banco de dados também saberão claramente onde as encontrar.

Usar objetos no lugar de registros, e classes no lugar de tabelas, traz um benefício extra: Permite a você inserir novos acessórios para suas tabelas. Por exemplo, se você tem uma tabelas chamada 'Cliente' com dois campos 'PrimeiroNome?' e 'Sobrenome', e você deseja trabalhar com uma única informação 'Nome'. No mundo orientado à objetos, se torna muito simples adicionar um novo método em uma classe Cliente.

public function getNome()
{
  return $this->getPrimeiroNome.' '.$this->getSobrenome();
}

Todas as funcionalidades de acesso aos dados e lógica de negócio do dado em si pode ser guardado nestes objetos. Imagine uma classe 'CarrinhoCompras?' que serve para guardar itens (também objetos). Para pegar o valor total de um carrinho de compras para um checkout, você pode adicionar o seguinte método:

public function getTotal()
{
  $total = 0;
  foreach ($this->getItems() as $item)
  {
    $total += $item->getPrice();
  }
  return $total;
}

E é isso. Imagine quanto tempo você demoraria para escrever uma consulta SQL fazendo a mesma coisa?

Propel é atualmente uma das melhores camadas de abstração objeto/relacional para PHP5. Ao invés de reescrevê-la, o symfony a integra ao framework.

Modelo de Dados

Proposta

Para criar um modelo de dados em objeto que o symfony irá utilizar, você precisa traduzir qualquer modelo de dados relacional que seu banco de dados possua para um modelo de dados em objeto. Isso é feito através de um arquivo schema chamado schema.yml, e nele serão definidas as tabelas, seus relacionamentos e o tipo de suas colunas.

O arquivo schema.yml deve estar localizado no diretório myproject/config/. Este arquivo possui uma sintaxe YAML, mas é muito simples de lê-lo sem ter qualquer conhecimento deste formato (que por acaso, leva 5 minutos para aprender).

Exemplo

Agora, vamos imaginar que você possui um banco de dados 'blog' que já contém registros em duas tabelas: 'blog_article' e 'blog_comment' que seguem a seguinte estrutura:

blog_article blog_comment
id id
title article_id
content author
created_at content
created_at

O schema.yml relacionado deve ser exatamente assim:

propel:

blog_article:

_attributes: { phpName: Article } id: title: varchar(255) content: longvarchar created_at:

blog_comment:

_attributes: { phpName: Comment } id: article_id: author: varchar(255) content: longvarchar created_at:

Notice that the name of the database itself (blog) doesn't appear in the schema.yml. Instead, the database is described under a connection name (propel in this example). This is because the actual connection settings can depend on the environment your application runs in. For instance, when you run your application in development, you will access a development database, but with the same schema as the production database, which has different connection settings. These settings will be specified in the databases.yml (see below).

Basic schema syntax

In a schema.yml, the first key represents a connection. It can contain several tables, each having a set of columns. According to the YAML syntax, the keys end with a colon, and the structure is shown through indentation (one or more spaces, but no tabulations).

A table can have special attributes, including the phpName describing the name of the class that will be generated. If you don't mention it, the name of the table will be used in place, after removing underscores and capitalizing the first letter of inner words (in the example, the default phpName of the tables would be blogArticle and blogComment). Columns also have a phpName, which is the capitalized version of the name (ID, TITLE, CONTENT, etc.) and doesn't need overriding in most cases.

Most of the time, columns only need one attribute: the type. It can take the usual values: boolean, integer, float, date, varchar(size), longvarchar, etc. For text content over 256 characters, you need to use the longvarchar type, which has no size but can not exceed 65Kb (MySQL specific). Note that the date and timestamp types have the usual limitations of Unix dates and can not be set to a date prior to 1970-01-01. As you may need to set older dates (for instance for dates of birth), a format of dates 'before Unix' can be used with bu_date and bu_timestamp.

Of course, if you want to specify your own primary keys, foreign-keys, default values or index, you can use the extended schema syntax (see below)

Schema conventions

Columns called id are considered to be primary keys.

Column ending with _id are considered to be foreign keys, and the related table is automatically determined according to the first part of the column name.

Columns called created_at are automatically set to the timestamp type.

For all these columns, you don't need to specify any type, since symfony will deduce their format from their name. That is why the schema.yml is so easy to write.

Object data model files

Code generation

Now that the data schema is described, the model classes are ready to be generated. In your project directory, type the command:

$ symfony propel-build-model

http://www.symfony-project.com/images/bubble.gif Note: Propel uses Phing at this point, so you will need to install it if it wasn't done already by calling:

    $ pear install http://phing.info/pear/phing-current.tgz

The base data access classes will be automatically created in the myproject/lib/model/om/ directory:

BaseArticle.php 
BaseArticlePeer.php 
BaseComment.php
BaseCommentPeer.php

In addition, the actual data access classes will be created in myproject/lib/model:

Article.php 
ArticlePeer.php 
Comment.php
CommentPeer.php

You only defined two tables and you end up with eight files. What's the deal here?

Base and current model

Why keep two versions of the data object model, one in model/om/ and another in model/?

You will probably need to add custom methods and attributes to the model objects (think about the ->getName() method that outputs the FirstName? and LastName? together). But as your project develops, you will also add tables or columns. Whenever you change the schema.yml, you will have to regenerate the object model classes by making a new call to symfony propel-build-model. If your custom methods were written in the classes actually genereated, then it would be erased after each generation.

The Base classes kept in the model/om/ directory are the ones generated by Propel. You should never modify them since every new build of the model will completely erase these files. But the regular object classes, kept in the model/ directory, actually inherit from the previous ones, and are never overwritten. So this is where you can add custom methods.

For example, here is the content of the newly created model/Article.php file:

require_once 'model/om/BaseArticle.php';
class Article extends BaseArticle
{
}

It inherits all the methods of the BaseArticle? class, but a modification in the model will not affect it. This structure is both customizable and evolutionary.

This mechanism allows you to start coding even without knowing the final relational model of your database.

Peer classes

The classes Article and Comment will allow you to access attributes of a record of the related tables. This means that you will be able to know the title of an article by calling a method of an Article object:

$article = new Article();
...
$title = $article->getTitle();

But there is more to databases than reading what's inside records: You also need to actually find records. That's the purpose of the Peer classes. They offer class methods to find records, that return an object or a lists of objects of the related 'no Peer' class.

$articles = ArticlePeer::retrieveByPks(array(123, 124, 125));
// $articles is an array of objects of class Article

From a data model point of view, note that there cannot be any Peer object. That's why the methods of the Peer classes will be called with a :: (for class method call) instead of the usual -> (for object method call).

Data access

In Propel, your data is accessed through objects. If you are used to the relational model, and to SQL to retrieve and alter your data, the Propel methods will probably look complicated. But once you've tasted the power of object orientation for data access (have a look at the examples given in the Propel documentation), you will probably like it a lot.

Field access

Propel provides one class for each the tables defined in the schema.yml file. These classes have default creators, accessors and mutators: The new, get and set methods give access to the columns of the table.

$article = new Article();
$article->setTitle('My first article');
$article->setContent('This is my very first article.\n Hope you enjoy it!');
 
$title   = $article->getTitle();
$content = $article->getContent();

Note that the phpName attribute of the table is used for the class name; the accessors use a CamelCase variant of the column names in which the first letter of each word is capitalized and underscores are suppressed.

To create a record in a table related to another, simply pass the object as a foreign key:

$comment = new Comment();
$comment->setAuthor('Steve');
$comment->setContent('Gee, dude, you rock: best article ever !');
$comment->setArticle($article);

The last line automatically sets the ArticleId? attribute of the Comment object to the value of the Id attribute of the $article object. Of course, you could have done it manually:

$comment->setArticleId($article->getId());

Note that by calling the new constructor, you created a new object but not an actual record in the Article table. In order to save the data into the database, you need to save() your object:

$article->save();

The cascading principle of Propel exempts you from also saving the $comment object: As the record is linked to the one you saved, it will be saved automatically.

Hint: To set several fields at one time, you can use the ->fromArray() methods of the Propel objects:

    $article->fromArray(array(
       'title'   =>   'My first article',
       'content' =>   'This is my very first article.\n Hope you enjoy it!',
    ));

Access to related records

Let's check all the comments about your new article:

$comments = $article->getComments();

Propel sees the article_id column in the Comment table, and understands that an article can have many comments (one-to-many relationship). During the code generation, a getComments() method was created in the Article object to get the array of related Comments. Note the plural used here in the get method. So the $comments variable contains an array of objects of class Comment, and you can display the first one with:

print_r($comments[0]);

If you read comments to your articles, you might change your mind about the interest of publishing on the Internet. And if you don't appreciate the irony of article reviewers, you can easily delete the comments with the delete() method:

foreach($comments as $comment)
{
  $comment->delete();
}

For many-to-one relationships, it's even simpler:

echo $comment->getArticle()->getTitle();

The getArticle() method returns an object of class Article, which benefits from the getTitle() accessor. This is much better than doing the join yourself, which may take a few lines of code (starting from the $comment->getArticleId() call).

Find more about these methods and their syntax in the Propel documentation.

Records access

If you know the primary key of a particular record, use the retrieveByPk() class method of the Peer class of the table to get the related object. For instance:

$article = ArticlePeer::retrieveByPk(7);

The schema.yml defines the id field as the primary key of the Article table, so this statement will actually return the article that has id number 7. As you used the primary key, you know that only one record will be returned. So the $article variable contains an object of the Article class.

For simple queries, Propel doesn't use SQL, to allow database abstraction. Instead, it offers a Criteria object, which has the same power and is very easy to use.

For instance, to retrieve all articles, type:

$c = new Criteria(); $articles = ArticlePeer::doSelect($c);

The class method doSelect() applies the selection filter received as a parameter. In this example, the $c object has no rules so the doSelect() call will return all the records of the Article table (it's equivalent to a 'SELECT *' in SQL). The variable $articles will contain an array of all the objects of class Article. To access them one by one, you can simply use a foreach statement:

foreach ($articles as $article) {

echo $article->getTitle();

}

But there is more to requests than getting all records or one record by its key. The power of the Criteria object is that you can add rules to it, just like you would add 'WHERE' or 'ORDER BY' statements in a SQL query.

To get all comments written by Steve, ordered by date, use:

$c = new Criteria(); $c->add(CommentPeer::AUTHOR, 'Steve'); $c->addAscendingOrderByColumn(CommentPeer::CREATED_AT); $comments = CommentPeer::doSelect($c);

Or, because you are upset about his tone, you may want to retrieve only the comments not written by Steve:

$c = new Criteria(); $c->add(CommentPeer::AUTHOR, 'Steve', Criteria::NOT_EQUAL); $c->addAscendingOrderByColumn(CommentPeer::DATE); $comments = CommentPeer::doSelect($c);

Note the class constants name used here: these are the phpNames of the columns in the object data model description. The ::doSelect() method will generate the best SQL query according to your database type, execute it, and return a list of objects - which are much better than record sets.

Note: Why use CommentPeer::AUTHOR instead of comment.AUTHOR, which is the way it will be output in the SQL query? Imagine that you have to change the name of the author field to contributor in the database. If you used comment.AUTHOR, you would have to change it in every call to the Propel objects in your code. By using CommentPeer::AUTHOR, you can simply change the column name in the schema.yml, and keep the phpName AUTHOR to prevent multiple modifications.

Refer to the Propel Documentation for more details about the Criteria object and its methods. created_at and updated_at columns

Usually, when a table has a created_at column, it is to store in this column a timestamp of the date when the record was created. The same applies to updated_at columns, that are usually updated each time the record itself is updated, to the value of the current time.

The good new is, symfony will recognize the name of these columns, and handle their update for you. You don't need to manually set the created_at and updated_at columns, they will automatically be updated.

$comment = new Comment(); $comment->setAuthor('Steve'); $comment->save(); // show the creation date echo $comment->getCreatedAt();

Additionally, the getters for these columns accept a date format as an argument:

echo $comment->getCreatedAt('Y-m-d');

Database access configuration

The data model is independent from the database used, but you will definitely use a database. The minimum information required by symfony to handle requests to the project database is the name, the access codes and the type of the database. This data should be entered in the databases.yml file located in the myproject/config/ directory:

prod:

propel:

param:

host: mydataserver username: myusername password: xxxxxxxxxx

all:

propel:

class: sfPropelDatabase param:

phptype: mysql host: localhost database: blog username: root password: compat_assoc_lower: true # datasource: propel

The database access is environment dependant. You can define different settings for the prod, dev, test environments, or any other environment you defined. The all header defines settings for all environments. This configuration can also be overridden per application, by setting different values in an application-specific file, for instance in myproject/apps/myapp/config/databases.yml.

Note: The host parameter can also be defined as hostspec.

For each environment, you can define many connections, each being linked to a schema.yml. Connections and schema are linked by the datasource parameter. It refers to the first key in the schema.yml. If you don't specify the datasource param, it takes the value of the label given to the connection settings.

The permitted values of the phptype parameter are the ones of the database systems supported by Propel:

  • mysql
  • sqlserver
  • pgsql
  • sqlite
  • oracle

The host, database, username and password settings define the parameters required to connect to the database.

In the above example, the settings for all environments can also be written using the shorthand syntax:

all:

propel:

class: sfPropelDatabase param:

dsn: mysql://root:@localhost/blog

Note: If you use a SQLite database, the host parameter has to be set to the path to the database file. For instance, if you keep your blog database in myproject/data/blog.db, the databases.yml will look like:

all:

propel:

class: sfPropelDatabase param:

dsn: sqlite://./../data/blog.db

Extended schema syntax Attributes

Database and tables can have specific attributes. They are set under an _attribute: key.

You may wish that your schema gets validated before code generation takes place. To do that, deactivate the noXSD attribute:

propel:

_attributes: { noXsd: false }

The main element supports the defaultIdMethodattribute; if none is provided then the databases native method of generating IDs will be used -- e.g. autoincrement for MySQL, sequences for PostgreSQL. The other possible value isnone`:

propel:

_attributes: { defaultIdMethod: none }

You already saw the phpName table attribute, used to set the name of the generated class mapping the table:

propel:

blog_article:

_attributes: { phpName: Article }

Tables that contain localized content (i.e., several versions of the content, in a related table, for internationalization) also take two additional attributes (see the internationalization chapter for details):

propel:

blog_article:

_attributes: { isI18N: true, i18nTable: db_group_i18n }

Column details

The basic syntax gives you two choices: let symfony deduce the column characteristics from its name (by giving an empty value) or define the type with one of the type keywords:

propel:

blog_article:

id: # let symfony do the work title: varchar(50) # specify the type yourself

But you can define much more for a column, and in this case you will need to define column settings as an associative array:

propel:

blog_article:

id: { type: integer, required: true, primaryKey: true, autoincrement: true } name: { type: varchar , size: 50, defaultValue: foobar, index: true } group_id: { type: integer, foreignTable: db_group, foreignReference: id, onDelete: cascade }

The column parameters are:

  • type: Propel culomn type. See Propel list of types for more details.
  • size: for VARCHAR type columns, the size of the string. Note that a column defined as varchar(50) in basic syntax is defined as { type: varchar , size: 50 } in extended syntax.
  • required: false by default, set it to true if you want the column to be required
  • defaultValue
  • primaryKey: boolean, to be set to true for primary keys
  • autoincrement: boolean, to be set to true for columns of type integer that need to be auto-increment
  • index: boolean, to be set to true if you want an index to be created on the column
  • foreignTable: to create a foreign key to another table.
  • foreignReference: the name of the related column if a foreign key is defined via foreignTable
  • onDelete: if set to cascade for a foreign key, records in this table are deleted when a related record in the foreign table is deleted.
  • isCulture: to be set to true for culture columns in localized content tables (see the internationalization chapter)

Foreign key

As an alternative to the foreignTable and foreignReference column attributes, you can add manually one or many foreign keys under the _foreign_keys key in a table:

propel:

blog_article:

id: title: varchar(50) user_id: { type: integer } _foreign_keys:

-

foreign_table: blog_user on_delete: cascade references:

  • { local: user_id, foreign: id }

This will create a foreign key on the user_id column, matching the id column in the blog_user table.

As compared to the column attributes, it is interesting for multiple-references foreign keys and to give foreign keys a name:

_foreign_keys:

my_foreign_key:

foreign_table: db_user onDelete: cascade references:

  • { local: user_id, foreign: id }
  • { local: post_id, foreign: id }

Index

As an alternative to the index column attribute, you can add manually one or many indexes under the _indexes key in a table:

propel:

blog_article:

id: title: varchar(50) created_at: _indexes:

my_index: [title, user_id] my_other_index: [created_at]

As for the foreign-key explicit syntax, it is only useful for indexes built on more than one column. Column automatisms

When meeting a column with no value, symfony will do some magic and add a value of its own:

id: { type: integer, required: true, primaryKey: true, autoincrement: true } foobar_id: { type: integer, foreignTable: db_foobar, foreignReference: id } created_at: { type: timestamp } updated_at: { type: timestamp }

For the foreign key automatism, symfony will look for a table having the same phpName as the beginning of the column name, and if one is found, it will take this table name as the foreignTable. Table automatisms (I18n)

Symfony supports content localization in related tables. This means that when you have content subject to localization, it is stored in two separated tables: one with the invariable columns, and another one with the localized columns.

In a schema.yml, all that is implied when you name a table foobar_i18n, as follows:

propel:

db_group:

id: - created_at: -

db_group_i18n:

name: varchar(50)

Symfony will automatically add the columns and table attributes to make the localized content mechanism work. This means that the previous schema is equivalent to:

propel:

db_group:

_attributes: { isI18N: true, i18nTable: db_group_i18n } id: - created_at: -

db_group_i18n:

id: { type: integer, required: true, primaryKey: true, foreignTable: db_group, foreignReference: id } culture: { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true } name: varchar(50)

Beyond the schema.yml: The schema.xml

As a matter of fact, the schema.yml format is internal to symfony. When you call a propel- command, symfony actually translates this file into a generated-schema.xml file, which is the type of file expected by Propel to actually perform tasks on the model.

The schema.xml files contain the same information as their YAML equivalent. This is what the Article/Comment schema defined previously looks like in XML format:

<?xml version="1.0" encoding="UTF-8"?>

<database name="propel" defaultIdMethod="native" noxsd="true">

<table name="blog_article" phpName="Article">

<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="title" type="varchar" size="255" /> <column name="content" type="longvarchar" /> <column name="created_at" type="timestamp" />

</table> <table name="blog_comment" phpName="Comment">

<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" /> <column name="article_id" type="integer" /> <foreign-key foreignTable="blog_article">

<reference local="article_id" foreign="id"/>

</foreign-key> <column name="author" type="varchar" size="255" /> <column name="content" type="longvarchar" /> <column name="created_at" type="timestamp" />

</table>

</database>

The description of the schema.xml format can be found in the documentation and the getting started sections of the Propel project website.

The YAML format was designed to keep the schemas simple to read and write, but the trade-off is that the more complex schemas can't be described with a schema.yml. On the other hand, the XML format allows for full schema description, whatever its complexity, and includes database vendor specific settings, table inheritance, etc.

Symfony actually understands schemas written in XML format. So if your schema is too complex for the YAML syntax, if you have an existing XML schema, or if you are already familiar with the Propel schema.xml syntax, you don't have to switch to the YAML syntax. Place your schema.xml in the project config/ directory, build the model, and there you go. Don't create the model twice Building a SQL database structure based on an existing schema

If you start your application by writing the schema.yml, you might not want to do it a second time in SQL to actually create the tables in the database. Symfony can generate a SQL query that creates the tables directly from the YAML data model. To get it, go to your root project directory and type:

$ symfony propel-build-sql

A schema.sql will be created in myproject/data/sql/. Note that the generated SQL code will be optimized for the database system defined in the phptype parameter of the databases.yml file. You can also precise the connection that you want to use in the propel.ini.

You can use the schema.sql directly to build the tables. For instance, in MySQL:

$ mysqladmin -u root -p create blog $ mysql -u root -p blog < data/sql/schema.sql

This command is also helpful to rebuild the database in another environment, or to change RDBMS.

Note: If the connection settings are properly defined in your propel.ini, you can even use the symfony propel-insert-sql command to do this automatically.

Generating an YAML data model from an existing database

Symfony can use the Creole database access layer to generate a schema.yml from an existing database, thanks to introspection. This can be particularly useful when you do reverse engineering, or if you prefer working on the database before working on the object model.

In order to do this, you have to make sure that the project propel.ini points to the correct database and contains all connection settings.

You can call the propel-build-schema command:

$ symfony propel-build-schema

And a brand new schema.yml built from your database structure is written in your myproject/config/ directory. You can build your model based on this structure.

The schema generation command is quite powerfull and can add to your schema a lot of database dependent information. As the YAML format doesn't handle this kind of vendor information, you need to generate a XML schema to take advantage of it. It is simply done by adding an argument to the build-schema task:

$ symfony propel-build-schema xml

Instead of generating a schema.yml file, this will create a schema.xml fully compatible with Propel, containing all the vendor infomation. Beware that generated XML schemas tend to be quite verbose and difficult to read. Multiple databases

As a matter of fact, you can setup several database connections for one project, to be able to dispatch your data into several databases. To that extent, you can write several schema.yml files (prefix them with a special name, but keep the schema.yml at the end so that they are recognized).

For instance, if you want to separate the connections for the blog and image repository parts of your website, create two files in your project config/ directory:

// in blog_schema.yml blog:

table1:

column1: ... column2: ...

//in image_schema.yml imagerep:

table1:

column1: ... column2: ...

Refactoring to the Data layer

When developing a symfony project, you often start by writing the domain logic code in the actions. As the projects is getting more complex, the actions contain a lot of PHP and SQL code, and become less readable.

At that point, all the logic related to the data should be moved to the Model layer. Whenever you need to do the same request in more than one place in your actions, think about transferring the related code to the Model. It helps to keep the actions small and readable.

For example, imagine the code needed in a weblog to retrieve the 10 most popular articles for a given tag (passed as request parameter). This code should not be in an action, but in the Model. As a matter of fact, if you need to display this list in a template, the action should simply look like:

public function executeShowPopularArticlesForTag() {

$tag = TagPeer::retrieveByName($this->getRequestParameter('tag')); $this->articles = $tag->getPopularArticles(10);

}

The action creates an object of class Tag from the request parameter. Then, all the code needed to query the database is located in a ->getPopularArticles() method of this class. Putting the code of this method in the action would make the code much less readable.

Moving code to a more appropriate location is one of the techniques of refactoring. If you do it often, your code will be easy to maintain and to understand by other developers. A good rule of thumb about when to do refactoring to the data layer is that the code of an action should rarely count more than ten lines of PHP code. Propel in symfony

All the details given above are not specific to symfony. As a matter of fact, you are not obliged to use Propel as your object/relational abstraction layer. But symfony works more seamlessly with Propel, since:

  • All the object data model classes and the Criteria classes are auto-loading classes. As soon as you will use them, symfony will include the right files, but you don't need to manually include the file inclusion.
  • In symfony, Propel doesn't need to be launched nor initialized. When an object uses Propel, the library initiates by itself.
  • Results of a request can be easily paginated (see more in the pager chapter).
  • Propel objects allow rapid prototyping and generation of a backend for your application (see more in the scaffolding chapter).
  • The schema.xml is faster to write through the schema.yml.

E, as Propel is independent of the database used, so is symfony.