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 with schema.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 a namespace 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 the om/ and map/ directories; this code would be lost next time you call the propel-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
    The schema.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 the Log 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 the propel-gen script: om, sql, and convert-conf. This is so usual that if you call the propel-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 !