Getting Started

This page gives a more or less detailed description about how to set up a new propel project on the basis of propel's example bookstore and shows propel-4's differences compared to propel-5.


Setting up a new project

If you installed Propel according to the Installation section you most likely ended up with a global installation prefix like usr/local or something similar. This makes it impossible for other users than root to build a project. Even if you installed Propel in a writable directory (e.g. inside your home directory) you may want to build your project in a different directory than propel/propel-generator/projects/YOUR-PROJECT.

Custom build file

For this we first create a new directory were the project should be placed. Inside this directory we then create a new file called build.xml. Just copy & paste the contents listed below and replace @PREFIX@ by your installation path to Propel.

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="project" default="main" basedir=".">
  <!-- set path to propel generator directory -->
  <property name="propel.generator.dir" value="@PREFIX@/propel/propel-generator/" />
  <!-- set a default target if none provided -->
  <property name="target" value="main" />
  <!-- load our project properties file -->
  <property file="./build.properties"/>

  <!-- propel target mappings -->
  <target name="main" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="${target}" />
  </target>
  <target name="convert-props" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="convert-props" />
  </target>
  <target name="create-db" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="create-db" />
  </target>
  <target name="creole" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="creole" />
  </target>
  <target name="datadtd" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="datadtd" />
  </target>
  <target name="datadump" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="datadump" />
  </target>
  <target name="datasql" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="datasql" />
  </target>
  <target name="insert-sql" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="insert-sql" />
  </target>
  <target name="om" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="om" />
  </target>
  <target name="sql" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="sql" />
  </target>
  <target name="graphviz" >
    <phing dir="${propel.generator.dir}" phingfile="build-propel.xml" 
           target="graphviz" />
  </target>
</project>

This file simply passes all phing targets to propel's build-propel.xml file after loading build.properties in this directory.

Specifying project properties

Next step is creating a build.properties for our project. A simple build.properties file would look like the following:

propel.project = bookstore
propel.targetPackage = bookstore

propel.database = mysql 
propel.database.createUrl = mysql://USER:PASS@localhost
propel.database.url = mysql://USER:PASS@localhost/bookstore

propel.targetPlatform = php4

propel.output.dir = ${project.basedir}
propel.schema.dir = ${project.basedir}
propel.conf.dir = ${project.basedir}

Note that all directory properties are set to ${project.basedir} which evaluates to our project directory. Of course, propel.targetPlatform must be set to php4 to get the propel-4 version of the generated templates ;)

Database schema and runtime configuration

Last step before building our project is creating database schema file schema.xml and the runtime configuration file runtime-config.xml. Since we use propel's bookstore example we can just copy those files from the bookstore directory over to our project directory. After setting the correct database values in runtime-conf.xml we are ready for build.

$ phing
$ phing create-db
$ phing insert-sql

A simple test script

Now that we successfully built our example project we can test if all works as expected with a simple script:

<?php

define('PREFIX',           '/home/micha/cvsroot/tigris/');
define('CREOLE_BASE',      PREFIX . 'creole/');
define('PROPEL_BASE',      PREFIX . 'propel/');
define('PROJECT_DIR',      dirname(__FILE__) . '/');
define('PROJECT_CONF',     PROJECT_DIR . 'conf/bookstore-conf.php');

$includes   = array();
$includes[] = CREOLE_BASE . 'creole-php4/classes/';
$includes[] = PROPEL_BASE . 'propel-php4/classes/';
$includes[] = PROJECT_DIR . 'classes/';
$includes[] = ini_get('include_path');

// don't forget to add current include_path
ini_set('include_path', implode(PATH_SEPARATOR, $includes));

require_once 'propel/Propel.php';
require_once 'bookstore/Author.php';

$e = Propel::init(PROJECT_CONF);
if (Propel::isError($e)) { die($e->getMessage()); }


// create new author
$a = new Author();
$a->setFirstName("Leo");
$a->setLastName("Tolstoy");

$e = $a->save();
if(Propel::isError($e)) { die($e->getMessage()); }

// create a second author
$b = new Author();
$b->setFirstName("John");
$b->setLastName("Smith");

$e = $b->save();
if(Propel::isError($e)) { die($e->getMessage()); }

// retrieve them from database
$rs =& AuthorPeer::doSelect(new Criteria());
if(Propel::isError($rs)) { die($rs->getMessage()); }

for($i = 0, $j = count($rs); $i < $j; $i++)
{
  $firstName = $rs[$i]->getFirstName();
  $lastName = $rs[$i]->getLastName();

  echo "Author: $firstName $lastName\n";
}

Executing the script would result in the following output:

$ php -f test.php
Author: Leo Tolstoy
Author: John Smith

Differences to propel-5

Propel-4 tries to be as equal as possible to Propel-5. Due to major changes in php5 this is not always possible, though. Below is a list of main differences between both implementations.

Error handling

Propel-4 uses the same exception classes which are known from Propel-5. The difference in Propel-4 is that exceptions are returned instead of thrown. This means that all class methods throwing an exception in Propel-5 return an exception class in Propel-4. Creole-4 and Propel-4 have a static method called isError, respectively. This method is used to check whether a return value is an exception.

  • Creole::isError($value) checks for exceptions returned by the creole layer (e.g. SQLException)
  • Propel::isError($value) checks for exceptions returned by propel itself (e.g. PropelException)

Example:

$criteria = new Criteria();
$criteria->addAscendingOrderByColumn(AuthorPeer::LAST_NAME());

$rs =& AuthorPeer::doSelect($criteria);
if(Propel::isError($rs)) { die($rs->getMessage()); }

Where possible class methods return TRUE on success.

$a = new Author();
$a->setFirstName("John");
$a->setLastName("Smith");

if(($e = $a->save()) !== true) {
  die($e->getMessage());
}

Accessing constants

Propel-5 constants like in the generated Peer classes or Criteria are static class methods in Propel-4.

Example:

$criteria = new Criteria();
$criteria->add(AuthorPeer::LAST_NAME(), "John", Criteria::EQUAL());

Optional Parameters

Optional parameters in Propel-5 are most likely used for passing a different Connection object to a class method. Since those Connection objects have to be passed as reference in Propel-4 this is not possible offhand. Therefore a new class Param has been introduced which is used to pass the connection as reference to the appropriate method.

Example

$con =& Propel::getConnection();
$rs =& AuthorPeer::doSelect(new Criteria(), Param::set($con));

With Param::set() a reference to $con is stored inside the Param object. The Param object itself is passed by value. However, php keeps the reference to the connection and inside doSave this reference is retrieved using the Param::get() method.