Propel 1.3にアップグレードする

このドキュメントはPropelバージョン1.2からバージョン1.3にアップグレードする方法を説明しています。

Propel1.3のAPIは1.2のAPIととても緊密である一方で、おそらくPropelを使用するコードに対して修正を要求するいくつかの重大な変更があります。

アップグレーディングに影響を与えるPropel1.3の新しい機能は次の通りです:

  1. 新しいPHPの必要要件
  2. Creoleの代わりにPDOを使用する
    1. ビルドとランタイムプロパティのための新しいDSN形式
    2. 新しいトランザクションAPI
    3. いくつかのメソッドシグネチャは変更しました
    4. 'mysqli'アダプタは時代遅れで削除されました
  3. 新しいSPLオートロードの統合

1.3のための要件

これはPropel1.3のランタイムとジェネレータコンポーネントのための要件です。1.2からの変更は太文字で示されていることに留意して下さい。

  • サポートされたデータベース (MySQL、MS SQL Server、PostgreSQL、SQLite、Oracle)
  • PHP 5.2.x. PHPは次のモジュールサポートが必要です:
    • XSLT (libxslt)
    • DOM (libxml2)
    • PDO (と望むデータベースのサポート)
    • SPL (PHP配布物の中にはSPLを明示的に有効にしなければなりません)
  • Phing 2.2.x
  • PEAR Logパッケージ (オプション)

Propel 1.3におけるPDO

パフォーマンスの増加と新しいPHP機能の利用とサポートをするために、Propel1.3はネイティブにPDOデータベース抽象化レイヤを使用します。この変更は、とりわけSQLを直接実行する人にとって多くの意味合いがあります。PDOのAPIはCreoleのものと緩やかに似ているので、この変更は大きな再設計を要求しません。

新しいDSN形式

build.propertiesruntime-conf.xmlファイルでPDO DSN形式を使用しなければなりません。

# build.propertiesにおけるデータベースの"url"は
# 新しい形式を使用しなければなりません。
propel.database.url = sqlite2:/tmp/bookstore.db

新しいDSNとオプションのPDOパラメータをサポートするためにruntime-conf.xmlファイルに<connection>要素への変更があります。

<config>
 <propel>
  <datasources default="bookstore">             
   <datasource id="bookstore">
    <!-- Propelアダプタ(通常はphptypeの接続DSNと同じです) -->
    <adapter>sqlite</adapter>                           
    <connection>
     <dsn>sqlite2:/tmp/bookstore.db</dsn>
     <!--
     MySQLとOracleのためにDSNとは別にユーザ名とパスワードを指定しなければなりません:
     <user>testuser</user>
     <password>password</password>
     -->
     <options>
      <option id="ATTR_PERSISTENT">false</option>
     </options>
    </connection>
   </datasource>
  </datasources>
 </propel>
</config>

新しいトランザクションAPI

PDOトランザクションAPIはCreoleよりもシンプルですがCreoleのConnection->begin()メソッドはPDO->beginTransaction()に代わります。現在Creoleで独自のトランザクションを管理している場合、これは明らかに影響があります。

言及する価値がある他の点はトランザクションが既に始まっているときにもう一つのトランザクションを始めようとするとネイティブのPDOオブジェクトは例外をスローすることです。このため、Propelはトランザクションコールをネストするためのサポートを提供するPDOPropelPDOサブクラスを使用します(実際には最外部のトランザクションだけが使用されますが)。

古い(Creoleの)コード:

<?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;
}

新しいPropelのメソッドシグネチャ

大方の予想通り、Connectionオブジェクトを受け取るために使用されるPropelメソッドはPDOオブジェクトを受け取ります。

他の変更はResultSet関連のPDOの欠落に関連しています。生成されたPeerのdoSelectRS()メソッドはdoSelectStmt()にリネームされました。PDOはResultSetクラスを持たないのでdoSelectStmt()は実行された命令文を返します。

Propel 1.2とCreoleのコード:

<?php

// オブジェクトを手作業でハイドレイトする方法の例
$rs = AuthorPeer::doSelectRS(new Criteria());
while($rs->next()) {
  $a = new Author();
  $a->hydrate($rs);
}

// 単独のカラムのjは井列を作成する方法の例
$rs = AuthorPeer::doSelectRS(new Criteria());
$names = array();
while($rs->next()) {
  $names[] = $rs->getString(2);
}

Propel 1.3とPDOコード:

<?php

// オブジェクトを手作業でハイドレイトする方法の例
$stmt = AuthorPeer::doSelectStmt(new Criteria());
while($row = $stmt->fetch(PDO::FETCH_NUM)) {
  $a = new Author();
  $a->hydrate($row);
}

// 単独のカラムの配列を作成する方法の例
$stmt = AuthorPeer::doSelectStmt(new Criteria());
$names = array();
while($res = $stmt->fetchColumn(1)) {
  $names[] = $res;
}

'mysqli'アダプタは削除されました

PDOを使用しているので、Propelのmysqliデータベースアダプタは削除されました。代わりにmysqlを使用して下さるようお願いします。

一般的なDB APIの変更

もちろん、アプリケーションが使用しているCreole APIのコード(例えばPropel::getConnection()から返される)はPDO APIを使用する必要があります。

PDO APIの詳細な情報に関してはhttp://www.php.net/pdoをご覧下さい。一般的に、PDO APIはCreoleのものととても似ていますので更新はとても簡単です。

例:

Propel 1.2とCreoleのコード:

<?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のコード:

<?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";
}

新しいSPLオートロードの統合

Propelのランタイムクラスと生成されたオブジェクトモデルクラスをロードするためにPropelはautoload()メソッドを提供しspl_autoload_registerを使用してそれを登録します。

この機能に関する重要な点です。

  • propel/Propel.phpと生成されたオブジェクトモデルクラス(例えばbookstore/Author.php)をインクルードできるように以前のようにinclude_pathをセットアップしなければなりません。
  • 既存の__autoload()メソッドがある場合、spl_autoload_registerを使用して登録しなければなりません
    • propel/Propel.phpをインクルードするときPropelのautoloadが登録されます
    • Phingのconvert-conf ターゲットは生成されたruntime-conf.phpファイルへのモデルクラスのマッピングを追加します。それがruntime-conf.phpファイルをリビルドする規範であることを意味します。

オブジェクトインスタンスプーリング

Propel 1.3において、オブジェクトインスタンスはデータベースから取得されるときにpeerクラスに"キャッシュ"されます。これが追加されたのは2つの理由がありました:

  1. オブジェクトが既にハイドレイトされた時に再びハイドレイトする必要をなくすため
  2. retrieveByPK()もしくはdoSelect*()への次のコールで同じオブジェクトのインスタンスを返すため

多くのユーザのためにこの変更された振る舞いはアプリケーションで少しかまったく影響がないかもしれません; 他のために、この新しい機能性はもっと一貫して期待され予期された振る舞いを表現します。

この機能性についていくつか重要なお知らせがあります:

  • データベースクエリは常に実行されます。一つのPHPセッション(リクエスト)が他に並行したセッションによって為される変更を追跡する方法がないからです; しかしながら、主キーがオブジェクトプールで見つかる場合ローカルオブジェクトは返されます(データベースから返されたオブジェクトにマッチしなくてもです)。このことはPropelのオブジェクトプーリングが現在のセッションの外部で削除された列を返さない一方で、ロッキングメカニズムの代わりにならないことを意味します。
  • ローカルに修正された(別名"汚された")オブジェクト(はデータベースへのコールによって返されます。例です:
    <?php
    
    $b = BookPeer::retrieveByPK(1);
    $b->setTitle("modified title (but not saved)");
       
    $b2 = BookPeer::retrieveByPK(1);
    print $b2->getTitle();
    // $b2 === $bなので"修正されたタイトル (保存されません)"を出力します
    
    

一対一のリレーションシップ

Propel 1.3は一対一のリレーションシップの概念を導入します。この機能のドキュメントに関してはチケット:279とwiki:Users/Documentation/1.3/Relationships#One-to-OneRelationshipsをご覧下さい。

オブジェクトモデルが一対一のリレーションシップを使用している場合この機能は後方互換性(BC)を壊す可能性があります。とりわけ、テーブルの主キーでもある外部キーを持つ場合、複数の結果が返される可能性がないのでPropelが生成したメソッドがPlural(複数形)の代わりに単数形になります。

例です(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>

古い (Propel 1.2) BookstoreEmployee:

<?php

class BaseBookstoreEmployee extends BaseObject {

  public function initBookstoreEmployeeAccounts() { /* ... */ }
  public function getBookstoreEmployeeAccounts($criteria, $con = null) { /* ... */ }
  public function addBookstoreEmployeeAccounts(BookstoreEmployeeAccount $l) { /* ... */ }

}

新しい (Propel 1.3) BookstoreEmployee:

<?php

class BaseBookstoreEmployee extends BaseObject {

  public function getBookstoreEmployeeAccount(PDO $con = null) { /* ... */ }
  public function setBookstoreEmployeeAccount(BookstoreEmployeeAccount $v) { /* ... */ }

}

大幅に改善された日付/時間の取り扱い

Propel 1.3は日付を内部で表現するために新しいPHP 5.2のDateTimeクラスを使用します。

オプションのAPIの変更

後方互換性を維持するために、次のビルドプロパティの設定(とOMのリビルド)をしない限り一時的な(temporal)アクセサメソッドはDateTimeオブジェクトを返しません:

propel.useDateTimeClass = true

propel.useDateTimeClassプロパティがtrueに設定されるとき、一時的なアクセサメソッドはDateTimeオブジェクトを返します。例です:

[デフォルトの]後方互換性モードで古いPropel 1.2 もしくはPropel 1.3:

<?php
$datestr = $obj->getTimestampColValue("Y-m-d H:i:s");

新しい Propel 1.3 w/ propel.useDateTimeClass=true:

<?php
$datestr = $obj->getTimestampColValue()->format("Y-m-d H:i:s");

  • 後方互換性の振る舞いはPropel2.0で廃止予定です; 一時的なアクセサメソッドからDateTimeオブジェクトを返すだけです。
  • ロケールセンシティブなstrftime()形式を使用したい場合、廃止予定の互換性モードを使用しなければなりません -- もしくはstrftime()をあなた自身で呼び出すことです。しかしこれは新しいDateTimeシステムでは動作しません。

プレ/ポストエポックの日付のサポート!

(propel.useDateTimeClass=trueを設定することで)Propelが常にDateTimeオブジェクトを取り扱うことを許可する場合、PHPの整数とUNIXエポックによってサポートされる範囲の前後の日付に関する特別な取り扱いにもはや悩む必要はありません。

ですので、アレクサンドロス大王の冒険の後を追うアプリケーションの構築を始めたい場合、BU_*タイプの定数を使用するもしくはPHPで厳密な文字列である日付を取り扱う必要はもはやありません。

デフォルトの値でサポートされる表現

このセクションは現在の1.3.0ベータ2の振る舞いについて説明しますが、1.3.0ファイルで変更されることが予期されますことに注意して下さい。デフォルトの値の表現サポートへの計画された改善に関してはticket:378をご覧下さい。

多くの要求された機能.... Propelでカラムのためにデフォルトの値として(CURRENT_TIMESTAMPのように)最後に表現することが出来ます。これをするために、<nowiki><column></nowiki>要素の新しい@defaultValueと@defaultExpr属性を導入しました:

<column name="colname" type="TIMESTAMP" defaultExpr="CURRENT_TIMESTAMP"/>

or

<column name="colname2" type="TIMESTAMP" defaultValue="2001-01-01"/>

古い@default属性はまだ動作しますが(@defaultValueの中を取る)、廃止予定で2.0で削除されます。