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.
