- Requirements for 1.3
- PDO in Propel 1.3
- New SPL Autoload Integration
- Object Instance Pooling
- One-to-One Relationships
- Much-Improved Date/Time Handling
- Expressions Supported in Default Values
- API Changes for Customizations / Extensions
- Base objects use constructors
- PostgreSQL uses SERIAL
- Criteria->addAsColumns() behavior change
Upgrading to Propel 1.3
This document describes how to upgrade from version 1.2 of Propel to version 1.3.
While the Propel 1.3 API is very close to the 1.2 API, there are a few significant changes that have been made which will probably require some modification to your code that uses Propel.
The new features in Propel 1.3 that will affect upgrading are:
- New PHP minimum requirements
- Use of PDO instead of Creole
- New DSN format for build and runtime properties
- New transaction API
- Some method signature changes
- 'mysqli' adapter is obsolete & has been removed
- New SPL autoload integration
- Some API changes for extending classes.
Requirements for 1.3
These are the requirements for the runtime and generator components of Propel 1.3. Note that changes from 1.2 requirements are indicated in bold.
- A supported database (MySQL, MS SQL Server, PostgreSQL, SQLite, Oracle)
- PHP 5.2.x. PHP needs to have the following module support:
- XSL extension (libxslt)
- DOM extension (libxml2)
- PDO (with support for your desired database)
- SPL (you must enable SPL explicitly in some PHP distributions)
- Phing 2.3.x
- PEAR Log package (optional)
PDO in Propel 1.3
In a move to increase performance and take advantage & support new PHP features, Propel 1.3 uses the native PDO database abstraction layer. This change has a number of implications, particularly for those who are executing SQL directly. PDO's API is loosely similar to Creole's, so this change shouldn't require any major re-architecture.
New DSN Format
You must use the PDO DSN format in your build.properties and runtime-conf.xml files.
# Any database "url" in build.properties # must use new format. propel.database.url = sqlite2:/tmp/bookstore.db
The runtime-conf.xml file has some changes to the <connection> element to support new DSN and optional PDO parameters.
<config>
<propel>
<datasources default="bookstore">
<datasource id="bookstore">
<!-- the Propel adapter (usually same as phptype of connection DSN) -->
<adapter>sqlite</adapter>
<connection>
<dsn>sqlite2:/tmp/bookstore.db</dsn>
<!--
For MySQL and Oracle you must specify username + password separate from DSN:
<user>testuser</user>
<password>password</password>
-->
<options>
<option id="ATTR_PERSISTENT">false</option>
</options>
</connection>
</datasource>
</datasources>
</propel>
</config>
New Transaction API
The PDO transaction API is similar to Creole's, but the Creole Connection->begin() method is PDO->beginTransaction() instead. This obviously will only affect you, if you are currently managing your own transactions with Creole.
Another point worth mentioning is that the native PDO object will throw an exception if you attempt to begin a transaction when one has already been started. For this reason, Propel uses a PropelPDO subclass of PDO that provides support for nesting transaction calls (though only the outer-most transaction is actually used).
Old (Creole) code:
<?php $con->begin(); try { /* db logic */ $con->commit(); } catch (SQLException $sqle) { $con->rollback(); throw $sqle; }
New (PDO) code:
<?php $con->beginTransaction(); try { /* db logic */ $con->commit(); } catch (PDOException $sqle) { $con->rollBack(); throw $sqle; }
New Propel Method Signatures
As expected, Propel methods that used to accept Connection objects now accept PDO objects.
Another change is related to PDO's lack of a ResultSet correlate. The generated Peer doSelectRS() method has been renamed doSelectStmt(). PDO does not have a ResultSet class, so the doSelectStmt() will return an executed statement.
Propel 1.2 + Creole code:
<?php // example of how to manually hydrate objects $rs = AuthorPeer::doSelectRS(new Criteria()); while($rs->next()) { $a = new Author(); $a->hydrate($rs); } // example of how to create array of single column $rs = AuthorPeer::doSelectRS(new Criteria()); $names = array(); while($rs->next()) { $names[] = $rs->getString(2); }
Propel 1.3 + PDO code:
<?php // example of how to manually hydrate objects $stmt = AuthorPeer::doSelectStmt(new Criteria()); while($row = $stmt->fetch(PDO::FETCH_NUM)) { $a = new Author(); $a->hydrate($row); } // example of how to create array of single column $stmt = AuthorPeer::doSelectStmt(new Criteria()); $names = array(); while($res = $stmt->fetchColumn(1)) { $names[] = $res; }
'mysqli' Adapter Removed
Since we are using PDO, Propel's mysqli database adapter has been removed. Please use mysql instead.
General DB API Changes
Of course, any code in your application that was using the Creole API (e.g. returned from Propel::getConnection()) will now need to use the PDO API.
See http://www.php.net/pdo for more information on the PDO API. Generally, the PDO API is quite similar to Creole's, so updates are generally quite easy.
For example:
Propel 1.2 + Creole code:
<?php $con = Propel::getConnection(SomeTablePeer::DATABASE_NAME); $stmt = $con->prepareStatement("SELECT * FROM some_table WHERE name = ?"); $stmt->setString(1, $name); $rs = $stmt->executeQuery(); while($rs->next()) { print "Name: " . $rs->getString("name") . "\n"; }
Propel 1.3 + PDO code:
<?php $con = Propel::getConnection(SomeTablePeer::DATABASE_NAME); $stmt = $con->prepare("SELECT * FROM some_table WHERE name = ?"); $stmt->bindValue(1, $name); $stmt->execute(); while($row = $stmt->fetch()) { print "Name: " . $row['name'] . "\n"; }
New SPL Autoload Integration
Propel now provides an autoload() method and registers it using spl_autoload_register for loading Propel runtime class and your generated object model classes.
Important points about this feature.'
- You still must setup your include_path as before so that you could include propel/Propel.php and your generated object model classes (e.g. bookstore/Author.php).
- If you have an existing __autoload() method, you must register it using spl_autoload_register
- The Propel autoload is registered when you include propel/Propel.php
- The convert-conf Phing target will add a mapping of your model classes to your generated runtime-conf.php file. This means that it is imperative that you rebuild your runtime-conf.php file.
Object Instance Pooling
In Propel 1.3, object instances are "cached" in the peer class when they are retrieved from the database. This was added for two reasons:
- To eliminate the need to re-hydrate the object when it has already been hydrated and
- To return the same object instance in subsequent calls to retrieveByPK() or doSelect*()
For many users this changed behavior may have little or no effect in their application; for others, this new functionality should present a more consistent and expected behavior.
A few important notes about this functionality:
- Database queries are always executed, because there is no way for one PHP session (request) to track changes that may have been made by other concurrent sessions; however, if the primary key is found in the object pool then the local object is returned (even though it may not match the object returned from the database). This means that while Propel's object pooling will not return rows that have been removed outside of current session, it is not a replacement for a locking mechanism.
- Locally modified (aka "dirty") objects will be returned by calls to the database. For example:
<?php $b = BookPeer::retrieveByPK(1); $b->setTitle("modified title (but not saved)"); $b2 = BookPeer::retrieveByPK(1); print $b2->getTitle(); // prints "modified title (but not saved)" since $b2 === $b
One-to-One Relationships
Propel 1.3 introduces the concept of one-to-one relationships. See ticket:279 and wiki:Users/Documentation/1.3/Relationships#One-to-OneRelationships for more documentation about this feature.
This feature has the potential to break BC if your object model uses one-to-one relationships. Specifically, if you have a foreign key that is also the primary key of your table the methods that Propel generates are going to be singular instead of Plural, since it would not be possible to have multiple results returned.
For example (simplified, from bookstore schema.xml):
<table name="bookstore_employee" description="Employees of a bookstore">
<column
name="id"
type="INTEGER"
primaryKey="true"
autoIncrement="true"
description="Employee ID number"/>
<column
name="name"
type="VARCHAR"
size="32"
description="Employee name"/>
</table>
<table name="bookstore_employee_account" description="Bookstore employees' login credentials">
<column
name="employee_id"
type="INTEGER"
primaryKey="true"
description="Primary key for "
/>
<column
name="login"
type="VARCHAR"
size="32"/>
<column
name="password"
type="VARCHAR"
size="100"/>
<foreign-key foreignTable="bookstore_employee" onDelete="cascade">
<reference local="employee_id" foreign="id"/>
</foreign-key>
</table>
OLD (Propel 1.2) BookstoreEmployee:
<?php class BaseBookstoreEmployee extends BaseObject { public function initBookstoreEmployeeAccounts() { /* ... */ } public function getBookstoreEmployeeAccounts($criteria, $con = null) { /* ... */ } public function addBookstoreEmployeeAccounts(BookstoreEmployeeAccount $l) { /* ... */ } }
NEW (Propel 1.3) BookstoreEmployee:
<?php class BaseBookstoreEmployee extends BaseObject { public function getBookstoreEmployeeAccount(PropelPDO $con = null) { /* ... */ } public function setBookstoreEmployeeAccount(BookstoreEmployeeAccount $v) { /* ... */ } }
Much-Improved Date/Time Handling
Propel 1.3 uses the new PHP 5.2 DateTime class to represent dates internally.
API Change
The temporal accessor methods will return DateTime objects if a null formatter is specified (or if the default is set to an empty string in your build.properties file). For BC, you can also disable the following build property (and rebuild your OM) (then the object classes will return unix timestamps as they used to do):
propel.useDateTimeClass = false
When the propel.useDateTimeClass property is set to true (the default), then the temporal accessor methods will return DateTime objects if no formatter string is specified. For example:
Propel 1.2 or Propel 1.3:
<?php $datestr = $obj->getTimestampColValue("Y-m-d H:i:s");
NEW Propel 1.3:
<?php $datestr = $obj->getTimestampColValue(null)->format("Y-m-d H:i:s");
- Note that the backwards-compatibility behavior is deprecated; with Propel 2.0, we will only return DateTime objects from temporal accessor methods.
- Also note that if you want use a string formatter for the locale-sensitive strftime() formatting, the date/time value will have to be converted to an integer timestamp (and therefore this will not worth with pre-/post-epoch values.
Support for pre-/post-Epoch Dates!
If you allow Propel to always deal with DateTime objects (by setting propel.useDateTimeClass=true), you no longer need to worry about any special handling for dates that are before or after the range supported by PHP integers +- the unix epoch.
So, if you want to start building an application that tracks the adventures of Alexander the Great, you no longer need to use the BU_* type constants or deal with those dates as strictly strings in PHP.
Expressions Supported in Default Values
Note, this section describes the current behavior as of 1.3.0beta2, but it is expected this will change before 1.3.0-final. See ticket:378 for the planed improvements to default value expression support.
A much requested feature.... You can finally expressions (like CURRENT_TIMESTAMP) as default values for columns in Propel. To do this, we introduced new @defaultValue and @defaultExpr attributes of of the <column> element:
<column name="colname" type="TIMESTAMP" defaultExpr="CURRENT_TIMESTAMP"/>
or
<column name="colname2" type="TIMESTAMP" defaultValue="2001-01-01"/>
The old @default attribute will still work (taking the meaning of @defaultValue), but it should be noted that this is deprecated and will be removed in 2.0.
API Changes for Customizations / Extensions
- The PHP5ComplexObjectBuilder and PHP5BasicObjectBuilder classes were merged into a single PHP5ObjectBuilder. If you had classes that extended PHP5ComplexObjectBuilder, you will want to update them to extend the PHP5ObjectBuilder.
- Similarly, the PHP5ComplexPeerBuilder and PHP5BasicPeerBuilder classese were merged into a single PHP5PeerBuilder. Update any subclasses accordingly. See ticket:511 for more details.
Base objects use constructors
The generated base objects set default values in their constructor currently. This means that if you define a custom constructor in your stub class, you must call the parent::construct() method.
<?php class MyObj extends BaseMyObj { public function __construct() { parent::__construct(); // other impl code here ... } }
Note that Propel will add these stub constructors by default (to serve as a reminder) when generating new objects; however, if you already have stub classes, you will need to be aware of this new behavior.
PostgreSQL uses SERIAL
Propel is now using the SERIAL pseudo type. While the API is largely the same, the SERIAL type defaults to a different sequence naming scheme than previous versions of Propel. This won't have any effect on new projects (unless you wish to directly reference the sequences, in which case you need to know that they're named differently); however, if you are upgrading an existing project to Propel 1.3, you'll need to do one of two things:
- Rename the sequences in your database to the new naming scheme or
- Override the sequence names in your schema.xml
Option 1: Rename sequences
Renaming sequences is relatively straightforward, but it must be done manually. The new sequence naming format is
<table name>_<col name>_seq.
In all but latest dev version of Postgres (8.3), you must use the ALTER TABLE command to rename sequences:
ALTER TABLE book_seq RENAME TO book_id_seq;
(It appears that for 8.3, Postgres introduced an ALTER SEQUENCE ... RENAME TO ... command.)
Option 2: Override sequence names
If you prefer not to change your database, you can override the sequence name in your schema.xml file. The
<id-method-parameter> tag exists for this purpose.
For example:
<table name="book"> <column name="id" type="integer" primaryKey="true" autoIncrement="true"/> <!-- ... add other columns ... --> <id-method-parameter value="book_seq"/> </table>
Criteria->addAsColumns() behavior change
With ticket:634, the behavior for As columns (added via Criteria->addAsColumn()) has changed slightly. "As" columns are now considered a valid select clause, and the generated Peer classes will not automatically add the Peer columns if there are already As clause elements present. While this allows you to use Criteria for queries that do not populate objects, it also requires that you add the select columns yourself if you do want to populate objects. Use the GeneratedPeer::addSelectColumns(Criteria) method to add the select columns.
For example:
<?php $c = new Criteria(MyPeer::DATABASE_NAME); MyPeer::addSelectColumns($c); $c->addAsColumn('colname', 'expr');
