sfPropel13Pluginを導入したときの注意点まとめ
symfony1.0.XでPropel1.3のDBレプリケーションが使いたい!という理由だけでsfPropel13Pluginを導入してみたので簡単に手順をまとめておきます。
インストール
SVNリポジトリからプラグイン本体をゲットすれば終了です。チェックアウトでもいいと思いますが、1.2ではデフォルトでPropel1.3が導入されていることから、もう更新はおそらく無いので自分のリポジトリに入れるようにexportしてます。
$ svn export http://svn.symfony-project.com/plugins/sfPropel13Plugin/ plugins/
導入(基本編)
まずdatabase.ymlをPropel1.3仕様に書き換えます。dsnの仕様はPHP: PDO_MYSQL DSN - Manualを参照してください。
all: propel: class: sfPropel13Database param: dsn: mysql:dbname=master;host=localhost username: foo password: foofoo instance-pooling: false slaves: slave1: dsn: mysql:dbname=slave_first;host=localhost username: bar password: barbar slave2: dsn: mysql:dbname=slave_second;host=localhost username: hoge password: hogehoge
propel.iniも1.3仕様に書き換えます。加えて、sfPropel13Pluginを参照するよう指定します。
propel.targetPackage = lib.model.propel propel.packageObjectModel = true propel.project = symfony propel.database = mysql propel.database.createUrl = mysql://foo:foofoo@localhost/ propel.addGenericAccessors = true propel.addGenericMutators = true propel.addTimeStamp = false propel.schema.validate = false ; sfPropel13Plugin propel.database.user = foo propel.database.password = foofoo propel.database.url = mysql:dbname=master;host=localhost propel.database.creole.url = mysql://foo:foofoo@localhost/master ; directories propel.home = . propel.output.dir = /home/Kiske/symfony propel.schema.dir = ${propel.output.dir}/config propel.conf.dir = ${propel.output.dir}/config propel.phpconf.dir = ${propel.output.dir}/config propel.sql.dir = ${propel.output.dir}/data/sql propel.runtime.conf.file = runtime-conf.xml propel.php.dir = ${propel.output.dir} propel.default.schema.basename = schema propel.datadump.mapper.from = *schema.xml propel.datadump.mapper.to = *data.xml ;sfPropel13Plugin builder settings propel.builder.peer.class = plugins.sfPropel13Plugin.lib.propel.builder.SfPeerBuilder propel.builder.object.class = plugins.sfPropel13Plugin.lib.propel.builder.SfObjectBuilder propel.builder.objectstub.class = addon.propel.builder.SfExtensionObjectBuilder propel.builder.peerstub.class = addon.propel.builder.SfExtensionPeerBuilder propel.builder.objectmultiextend.class = addon.propel.builder.SfMultiExtendObjectBuilder propel.builder.mapbuilder.class = addon.propel.builder.SfMapBuilderBuilder propel.builder.interface.class = propel.engine.builder.om.php5.PHP5InterfaceBuilder propel.builder.node.class = propel.engine.builder.om.php5.PHP5NodeBuilder propel.builder.nodepeer.class = propel.engine.builder.om.php5.PHP5NodePeerBuilder propel.builder.nodestub.class = propel.engine.builder.om.php5.PHP5ExtensionNodeBuilder propel.builder.nodepeerstub.class = propel.engine.builder.om.php5.PHP5ExtensionNodePeerBuilder propel.builder.addIncludes = false propel.builder.addComments = false propel.builder.addBehaviors = true ;using innodb propel.mysql.tableType = InnoDB
セッションをMySQLに格納するようにしているのであればstorageクラスも変更します。apps/[app_name]/config/factories.ymlを修正します。
storage: class: sfPDOSessionStorage param: session_name: symfony database: propel db_table: session_storage db_data_col: session_data db_id_col: session_id db_time_col: session_time
設定ファイルの修正は以上です。
ModelオブジェクトをPropel1.3でリビルドします。
$ symfony propel-13-build-model
次にModelオブジェクトを修正していきます。Baseのまま利用しているのはリビルドで書き換えてくれるので自前のModelオブジェクトが修正対象となります。注意点をリストアップします。サンプルコードはPropelの公式ドキュメントから引用しています。
- saveメソッドの引数のオブジェクトのタイプヒンディング指定
<?php public function save(PropelPDO $con = null) { // save method... }
- doSelectRS -> doSelectStmtに変更
<?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; }
- begin メソッドが beginTransaction に変更
<?php $con->beginTransaction(); try { /* db logic */ $con->commit(); } catch (PDOException $sqle) { $con->rollBack(); throw $sqle; }
- プリペアドステートメンドの文法の変更
<?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"; }
導入(応用編)
今まではPropel1.3にする上で必ず必要となる項目でしたが、ここからは個人的に嵌ったところを書きます。
カスタムクエリをPagerに投げるとき
今まではsfAdvancedPropelPagerというsymfony公式のCode Snippetに書かれてるカスタムクラスを使っていたのですが、当然ながらsfPropel13Pluginには対応してないので動きません。
思いついた解決方法は3つでした。
- sfAdvancedPropelPagerをPropel1.3に対応するようカスタマイズ
- Criteria::CUSTOMを使う
- プリペアドステートメントでデータ処理してPager部分は内製(or ライブラリ利用)
自分はCriteria::CUSTOMで回避しています。文字列エスケープはPDO::quoteを使っています。
sfPropelParanoidPluginが動かなくなる
DBを論理削除してくれるようになるプラグインですが、そのままでは動きませんでした。symfony1.2用にリリースされているバージョンを参考に修正しました。
- plugins/sfPropelParanoidBehaviorPlugin/config/config.php
<?php sfPropelBehavior::registerMethods('paranoid', array( array('sfPropelParanoidBehavior', 'forceDelete'), )); sfPropelBehavior::registerHooks('paranoid', array( ':delete:pre' => array('sfPropelParanoidBehavior', 'preDelete'), 'Peer:doSelectStmt' => array('sfPropelParanoidBehavior', 'doSelectStmt'), 'Peer:doSelectJoin' => array('sfPropelParanoidBehavior', 'doSelectStmt'), 'Peer:doSelectJoinAll' => array('sfPropelParanoidBehavior', 'doSelectStmt'), 'Peer:doSelectJoinAllExcept' => array('sfPropelParanoidBehavior', 'doSelectStmt'), ));
- plugins/sfPropelParanoidBehaviorPlugin/lib/sfPropelParanoidBehavior.class.phpの22行目
public function doSelectStmt($class, $criteria, $con = null) { // ... }
レプリケーション設定
デフォルトだとsfPropel13Pluginはレプリケーションに対応していないようなので公式フォーラムのsymfony framework forum: General plug-ins => [sfPropel13Plugin] Propel replication support? SOLVEDを見て解決しました。Forumのやり方だとプラグインとフレームワーク本体を直接編集することになるので抵抗がある人は app/[app_name]/lib/ 下にカスタムクラスでオーバーライドすれば良いかと思います。
特にsymfony本体側は更新が今後もありそうなのでカスタムクラス化した方がよいかと思います。編集するのは[sf_symfony_lib_dir]/config/sfDatabaseConfigHandler.class.phpです。カスタムクラス化したときは app/[app_name]/config/config_handers.ymlを作成し下記コードを記述してカスタムクラスをロードするようにしてください。
config/databases.yml: class: myDatabaseConfigHandler
以上です。案外すんなり導入できるので興味がある人は試してみてください。PDOなのでCreoleよりはおそらく処理速度も上がっているはずです。(実測はしてません)
しかし、一番ベストなのはsymfony1.2へ移行するほうが良いと思います。