Symfony model
Overview
Symfony has an object/relational abstraction layer based on the Propel project. Accessing data stored in a database and modifying it is made easy through an object translation of the database. This chapter explains the creation of such an object data model, the way to access and modify the data in Propel. It also illustrates the integration of Propel in Symfony.
Why use an abstraction layer?
Databases are relational. PHP5 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. It is called an object-relational mapping, or ORM.
This is the Model layer of the symfony MVC architecture. It 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 hasn't 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. Just change one line in a configuration file, and it works.
An abstraction layer encapsulates the data logic. The rest of the application doesn't need to know about the SQL queries, and the SQL that accesses the database is easy to find. Developers who are specialized in database programming also know clearly where to go.
Using objects instead of records, and classes instead of tables, have another benefit. They allow you to 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, it is as easy as adding a new accessor method to the Client class.
public function getName()
{
return $this->getFirstName.' '.$this->getLastName();
}
All the repeated data access functions and the business logic of the data itself can be kept in such objects. Imagine a class 'ShoppingCart' in which you keep items (which are objects). To get the full amount of the shopping cart for the checkout, you can add the following method:
public function getTotal()
{
$total = 0;
foreach ($this->getItems() as $item)
{
$total += $item->getPrice();
}
return $total;
}
And that's it. Imagine how long it would have required to write a SQL query doing the same?
Propel is currently one of the best object/relational abstraction layer for PHP5. Instead of rewriting it, symfony integrates it seamlessly into the framework.
Data model
Purpose
In order to create the data object model that symfony will use, you need to translate whatever relational model your database has to an object data model. This is done through a schema file called schema.yml, in which are defined the tables, their relations and the type of their columns.
The schema.yml file must be located in the myproject/config/ directory. This file uses the YAML syntax, but it is simple to
read without any knowledge of that format (which, by the way, takes 5 minutes to learn).
Example
Now, let's imagine that you have a database 'blog' already containing data in two tables: 'blog_article' and 'blog_comment' with the following structure:
| blog_article |
blog_comment |
| id |
id |
| title |
article_id |
| content |
author |
| created_at |
content |
|
created_at |
The related schema.yml should simply look like:
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). It is possible to have multiple schema.yml files for your project, please see the notes below on foreign keys to ensure that you can reference foreign tables in seperate schema files.
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
Empty columns called id are considered to be primary keys.
Empty 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.
Empty 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
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 generated, 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 news 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: oxyscrip.ipowermysql.com
database: blog
username: root
password:
compat_assoc_lower: true
# datasource: propel
# encoding: utf8
# persistent: true
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.
Note: The persistent parameter can also be defined to enable persistent database connections.
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:@oxyscrip.ipowermysql.com/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 }
If you use plugins, or wish to use multiple schema.yml files, then the databases in each file must be related by the package attribute to allow you to reference foreign keys on tables defined in seperate schema.yml files. By default all databases are declared to be part of the lib.model package. If you declare tables to belong to sub packages (as in plugins) then these files will be generated there and not in lib.model.
propel:
_attributes: { package: lib.model }
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, default: foobar, index: true }
group_id: { type: integer, foreignTable: db_group, foreignReference: id, onDelete: cascade }
The column parameters are:
type: Propel column type. See Propel list of types for more details. The compact syntax (varchar(50)) is also supported as a type value.
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
default: default value
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
sequence: sequence name for databases using sequences for auto-increment columns (e.g. PostgreSQL or Oracle)
index: boolean, to be set to true if you want a simple index or to unique if you want a unique 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 }
Note: Foreign keys between tables defined in separate schema.yml files must be explicitly defined as outlined above as they cannot be auto detected by symfony. You must also ensure that you have declared each database to belong to the same package (see the 'Attributes' section above), and that you have the statement propel.packageObjectModel = true in your propel.ini file (these settings are now default in symfony projects).
Index
As an alternative to the index column attribute, you can add manually one or many indexes under the _indexes key in a table. If you want to define unique indexes, you have to use the _uniques header instead:
propel:
blog_article:
id:
title: varchar(50)
created_at:
_indexes:
my_index: [title, user_id]
_uniques:
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 and a name it recognizes, 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 (which works for all columns ending with _id), 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.
Additionally, if a table doesn't define any primary key, symfony will automatically add an id column as an autoincremental integer.
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, onDelete: cascade }
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 lib.model.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 lib.model.schema.sql directly to build the tables. For instance, in MySQL:
$ mysqladmin -u root -p create blog
$ mysql -u root -p blog < data/sql/lib.model.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 information. 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.
And, as Propel is independent of the database used, so is symfony.
|