= 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: 1. New PHP minimum requirements 1. Use of PDO instead of Creole 1. New DSN format for build and runtime properties 1. New transaction API 1. Some method signature changes 1. 'mysqli' adapter is obsolete & has been removed 1. New SPL autoload integration 1. 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) * '''[http://www.php.net/ 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) * '''[http://phing.info/ 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 [http://www.php.net/pdo 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 [http://php.net/manual/en/function.PDO-construct.php PDO DSN] format in your {{{build.properties}}} and {{{runtime-conf.xml}}} files. {{{ #!sh # 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 element to support new DSN and optional PDO parameters. {{{ #!xml sqlite sqlite2:/tmp/bookstore.db }}} === 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 begin(); try { /* db logic */ $con->commit(); } catch (SQLException $sqle) { $con->rollback(); throw $sqle; } }}} '''New''' (PDO) code: {{{ #!php 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 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 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 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 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 [http://www.php.net/spl-autoload-register 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 [http://www.php.net/spl-autoload-register 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: 1. To eliminate the need to re-hydrate the object when it has already been hydrated and 1. 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 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: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}}}): {{{ #!xml
}}} '''OLD (Propel 1.2) BookstoreEmployee:''' {{{ #!php getTimestampColValue("Y-m-d H:i:s"); }}} '''NEW Propel 1.3:''' {{{ #!php 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 element: {{{ #!xml }}} or {{{ #!xml }}} 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 ''_''''_seq. In all but latest dev version of Postgres (8.3), you must use the '''ALTER TABLE''' command to rename sequences: {{{ #!sql 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 '''''' tag exists for this purpose. For example: {{{ #!xml
}}} == 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 addAsColumn('colname', 'expr'); }}}