The Build Time
The initial step in every Propel project is the "build". During build time, a developer describes the structure of the datamodel in a XML file called the "schema". From this schema, Propel generates PHP classes, called "model classes", made of object-oriented PHP code optimized for a given RDMBS. The model classes are the primary interface to find and manipulate data in the database in Propel.
The XML schema can also be used to generate SQL code to setup your database. Alternatively, you can generate the schema from an existing database (see the Existing-Database reverse engineering chapter for more details), or from a DBDesigner 4 model (see the DBDesigner2Propel chapter).
During build time, a developer also defines the connection settings for communicating with the database.
To illustrate Propel's build abilities, this chapter uses the data structure of a bookstore as an example. It is made of three tables: a book
table, with a foreign key to two other tables, author
and publisher
.
Describing Your Database as XML Schema
Propel generates PHP classes based on a relational description of your data model. This "schema" uses XML to describe tables, columns and relationships. The schema syntax closely follows the actual structure of the database.
Create a bookstore
directory. This will be the root of the bookstore project.
Database Connection Name
Create a file called schema.xml
in the new bookstore/
directory.
The root tag of the XML schema is the <database>
tag:
<?xml version="1.0" encoding="UTF-8"?>
<database name="bookstore" defaultIdMethod="native">
<!-- table definitions go here -->
</database>
The name
attribute defines the name of the connection that Propel uses for the tables in this schema. It is not necessarily the name of the actual database. In fact, Propel uses a second file to link a connection name with real connection settings (like database name, user and password). This runtime-conf.xml
file will be explained later in this chapter.
The defaultIdMethod
attribute indicates that the tables in this schema use the database's "native" auto-increment/sequence features to handle id columns that are set to auto-increment.
Tip
You can define several schemas for a single project. Just make sure that each of the schema filenames end withschema.xml
.
Tables And Columns
Within the <database>
tag, Propel expects one <table>
tag for each table:
<?xml version="1.0" encoding="UTF-8"?>
<database name="bookstore" defaultIdMethod="native">
<table name="book" phpName="Book">
<!-- column and foreign key definitions go here -->
</table>
<table name="author" phpName="Author">
<!-- column and foreign key definitions go here -->
</table>
<table name="publisher" phpName="Publisher">
<!-- column and foreign key definitions go here -->
</table>
</database>
This time, the name
attributes are the real table names. The phpName
is the name that Propel will use for the generated PHP class. By default, Propel uses a CamelCase version of the table name as its phpName - that means that you could omit the phpName
attribute in the example above.
Within each set of <table>
tags, define the columns that belong to that table:
<?xml version="1.0" encoding="UTF-8"?>
<database name="bookstore" defaultIdMethod="native">
<table name="book" phpName="Book">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="isbn" type="varchar" size="24" required="true" phpName="ISBN"/>
<column name="publisher_id" type="integer" required="true"/>
<column name="author_id" type="integer" required="true"/>
</table>
<table name="author" phpName="Author">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="first_name" type="varchar" size="128" required="true"/>
<column name="last_name" type="varchar" size="128" required="true"/>
</table>
<table name="publisher" phpName="Publisher">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="name" type="varchar" size="128" required="true" />
</table>
</database>
Each column has a name
(the one used by the database), and an optional phpName
attribute. Once again, the Propel default behavior is to use a CamelCase version of the name
as phpName
when not specified.
Each column also requires a type
. The XML schema is database agnostic, so the column types and attributes are probably not exactly the same as the one you use in your own database. But Propel knows how to map the schema types with SQL types for many database vendors. Existing Propel column types are boolean
, tinyint
, smallint
, integer
, bigint
, double
, float
, real
, decimal
, char
, varchar
, longvarchar
, date
, time
, timestamp
, blob
, clob
, object
, and array
. Some column types use a size
(like varchar
and int
), some have unlimited size (longvarchar
, clob
, blob
). Check the schema reference for more details on each column type.
As for the other column attributes, required
, primaryKey
, and autoIncrement
, they mean exactly what their names imply.
Tip
Propel supports namespaces (for PHP > 5.3). If you specify anamespace
attribute in a<table>
element, the generated PHP classes for this table will use this namespace.
Foreign Keys
A table can have several <foreign-key>
tags, describing foreign keys to foreign tables. Each <foreign-key>
tag consists of one or more mappings between a local column and a foreign column.
<?xml version="1.0" encoding="UTF-8"?>
<database name="bookstore" defaultIdMethod="native">
<table name="book" phpName="Book">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="title" type="varchar" size="255" required="true" />
<column name="isbn" type="varchar" size="24" required="true" phpName="ISBN"/>
<column name="publisher_id" type="integer" required="true"/>
<column name="author_id" type="integer" required="true"/>
<foreign-key foreignTable="publisher" phpName="Publisher" refPhpName="Book">
<reference local="publisher_id" foreign="id"/>
</foreign-key>
<foreign-key foreignTable="author">
<reference local="author_id" foreign="id"/>
</foreign-key>
</table>
<table name="author" phpName="Author">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true"/>
<column name="first_name" type="varchar" size="128" required="true"/>
<column name="last_name" type="varchar" size="128" required="true"/>
</table>
<table name="publisher" phpName="Publisher">
<column name="id" type="integer" required="true" primaryKey="true" autoIncrement="true" />
<column name="name" type="varchar" size="128" required="true" />
</table>
</database>
A foreign key represents a relationship. Just like a table or a column, a relationship has a phpName
. By default, Propel uses the phpName
of the foreign table as the phpName
of the relation. The refPhpName
defines the name of the relation as seen from the foreign table.
There are many more attributes and elements available to describe a datamodel. Propel's documentation provides a complete Schema of the schema syntax, together with a DTD and a XSD schema for its validation.
Building The Model
Setting Up Build Configuration
The build process is highly customizable. Whether you need the generated classes to inherit one of your classes rather than Propel's base classes, or to enable/disable some methods in the generated classes, pretty much every customization is possible. Of course, Propel provides sensible defaults, so that you actually need to define only two settings for the build process to start: the RDBMS you are going to use, and a name for your project.
Propel expects the build configuration to be stored in a file called build.properties
, and stored at the same level as the schema.xml
. Here is an example for a MySQL database:
# Database driver
propel.database = mysql
# Project name
propel.project = bookstore
Use your own database vendor driver, chosen among pgsql, mysql, sqlite, mssql, and oracle.
You can learn more about the available build settings and their possible values in the build configuration reference.
Using the propel-gen
Script To Build The Model
The Propel generator uses the propel-gen
script, as seen in the previous chapter. This executable expects a command name as its argument.
Open a terminal and browse to the bookstore/
directory, where you saved the two previous files (schema.xml
, and build.properties
). Then use the propel-gen
script to call the "Object Model generator" command using its shortcut - "om":
cd /path/to/bookstore
propel-gen om
You should normally see a some colored lines appear in the terminal, logging all the class generation, and ending with "BUILD FINISHED". If not, look for red lines in the log and follow the directions in the error messages.
Generated Object Model
The "om" command added a new directory in the bookstore/
project, called build/
. The generated model classes are located under the classes/bookstore/
subdirectory:
> cd /path/to/bookstore
> cd build/classes/bookstore/
> ls
om/
map/
Author.php
AuthorPeer.php
AuthorQuery.php
Book.php
BookPeer.php
BookQuery.php
Publisher.php
PublisherPeer.php
PublisherQuery.php
For every table in the database, Propel creates 3 PHP classes:
- a model class (e.g.
Book
), which represents a row in the database; - a peer class (e.g.
BookPeer
), offering static constants and methods mostly for compatibility with previous Propel versions; - a query class (e.g.
BookQuery
), used to operate on a table to retrieve and update rows
Propel uses the phpName
attribute of each table as the base for the PHP class names.
All these classes are empty, but they inherit from Base
classes that you will find under the om/
directory. For instance, if we follow our previous example with the book, the file /path/to/bookstore/build/classes/bookstore/Book.php
could looks like this :
```php
<?php
/** * Skeleton subclass for representing a row from the 'book' table. */ class Book extends BaseBook { } ```
These empty classes are called stub classes. This is where you will add your own model code. These classes are generated only once by Propel ; on the other hand, the base classes they extend are overwritten every time you call the om
command, and that happens a lot in the course of a project, because the schema evolves with your needs.
In addition to these classes, Propel generates one TableMap
class for each table under the map/
directory. You will probably never use the map classes directly, but Propel needs them to get metadata information about the table structure at runtime.
Tip
Never add any code of your own to the classes generated by Propel in theom/
andmap/
directories; this code would be lost next time you call thepropel-gen
script.
Basically, all that means is that despite the fact that Propel generates seven classes for each table, you should only care about two of them: the model class and the query class.
Building The Database
To save you the burden of defining your model twice, Propel can initialize a database based on the schema, by creating the tables and foreign keys.
Building The SQL File
Once again, use the propel-gen
script to generate the SQL files necessary to create the tables, this time with the "sql" command:
cd /path/to/bookstore
propel-gen sql
The generated SQL definition can be found in the build/sql/schema.sql
file. The code is optimized for the database driver defined in the build.properties
.
Using The SQL File
Create the database and setup the access permissions using your favorite database client. For instance, to create the my_db_name
database with MySQL, type:
mysqladmin -u root -p create my_db_name
Now you can use the generated code directly:
mysql -u root -p my_db_name < build/sql/schema.sql
Tip
Theschema.sql
file will DROP any existing table before creating them, which will effectively erase your database.
Depending on which RDBMS you are using, it may be normal to see some errors (e.g. "unable to DROP...") when you first run this command. This is because some databases have no way of checking to see whether a database object exists before attempting to DROP it (MySQL is a notable exception). It is safe to disregard these errors, and you can always run the script a second time to make sure that the errors are no longer present.
Inserting SQL With propel-gen
As an alternative to using the generated sql code directly, you can ask Propel to insert it directly into your database. Start by defining the database connection settings in the build.properties
, as follows:
# Connection parameters
propel.database.url = mysql:host=localhost;dbname=my_db_name
propel.database.user = my_db_user
propel.database.password = my_db_password
# Other examples:
# propel.database.url = sqlite:/path/to/bookstore.db
# propel.database.url = pgsql:host=localhost dbname=my_db_name user=my_db_user password=my_db_password
The propel.database.url
setting should be a PDO DSN (see the PDO documentation for more information about vendor-specific DSN). The user
and password
are only necessary for the mysql
and oracle
drivers.
Then use the propel-gen
script with the "insert-sql" command to connect to the database and inject the generated SQL code:
cd /path/to/bookstore
propel-gen insert-sql
Runtime Connection Settings
The database and PHP classes are now ready to be used. But they don't know yet how to communicate with each other at runtime. You must add a configuration file so that the generated object model classes and the shared Propel runtime classes can connect to the database, and log the Propel activity.
Writing The XML Runtime Configuration
Create a file called runtime-conf.xml
at the root of the bookstore
project, using the following content:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- Uncomment this if you have PEAR Log installed
<log>
<type>file</type>
<name>/path/to/propel.log</name>
<ident>propel-bookstore</ident>
<level>7</level>
</log>
-->
<propel>
<datasources default="bookstore">
<datasource id="bookstore">
<adapter>mysql</adapter> <!-- sqlite, mysql, mssql, oracle, or pgsql -->
<connection>
<dsn>mysql:host=localhost;dbname=my_db_name</dsn>
<user>my_db_user</user>
<password>my_db_password</password>
</connection>
</datasource>
</datasources>
</propel>
</config>
Notice how the id
attribute of the <datasource>
tag matches the connection name defined in the <database>
tag of the schema.xml
. This is how Propel maps a database description to a connection.
Replace the <adapter>
and the <connection>
settings with the ones of your database.
See the runtime configuration reference for a more detailed explanation of this file.
Tip
If you uncomment the<log>
section, Propel will attempt to instantiate theLog
class (from the PEAR Log package) with the specified parameters and use that to log queries. Propel's statement logging happens at the DEBUG level (7); errors and warnings are logged at the appropriate (non-debug) level.
Building the Runtime Configuration
For performance reasons, Propel prefers to use a PHP version of the connection settings rather than the XML file you just defined. So you must use the propel-gen
script one last time to build the PHP version of the runtime-conf.xml
configuration:
cd /path/to/bookstore
propel-gen convert-conf
The resulting file can be found under build/conf/bookstore-conf.php
, where "bookstore" is the name of the project you defined in build.properties
.
Tip
As you saw, a Propel project setup requires that you call three commands with thepropel-gen
script:om
,sql
, andconvert-conf
. This is so usual that if you call thepropel-gen
script with no parameter, it will execute these three commands in a row:
cd /path/to/bookstore
propel-gen
Setting Up Propel
This is the final step: initialize Propel in your PHP script. You may wish to do this step in an init or setup script that is included at the beginning of your PHP scripts.
Here is a sample initialization file:
<?php
// Include the main Propel script
require_once '/path/to/propel/runtime/lib/Propel.php';
// Initialize Propel with the runtime configuration
Propel::init("/path/to/bookstore/build/conf/bookstore-conf.php");
// Add the generated 'classes' directory to the include path
set_include_path("/path/to/bookstore/build/classes" . PATH_SEPARATOR . get_include_path());
Now you are ready to start using your model classes!
Tip
If you installed Propel using PEAR, you can replace the first line of this script with the more simple:
<?php
// Include the main Propel script
require_once 'propel/Propel.php';
Found a typo ? Something is wrong in this documentation ? Just fork and edit it !