FuelPHPのormを複数DBに対応させる方法

FuelPHP Advent Calendar 2012 20日目の担当の@6q5です。

昨日は、@ttikittFuelPHPへのDoctrine2組み込みでした。

今年は仕事で3つもFuelPHPを使った媒体に関わり、
FuelPHPと共に過ごした1年でした。
その経験で得た物の中から選りすぐりの1つ?を紹介します。

ormでDBクラスのexecuteのようにDB接続先を指定することができません。

DBがMaster/Slave構成の時に、ormを使って参照だけの時はSlaveに向けたいなどしたい時に不便です。

例 config/db.php こんな感じでconfigに2つのDB設定がされてるとします。
/**
 * The development database settings.
 */
return array(
    'default' => array(
        'connection' => array(
            'dsn' => 'mysql:host=xxx.xxx.xxx.xxx;dbname=BLOG_DEV',
            'username' => 'blog',
            'password' => 'xxxxxxxx',
        ),  
    ),
    'slave' => array(
        'connection' => array(
            'dsn' => 'mysql:host=xxx.xxx.xxx.xxx;dbname=BLOG_DEV',
            'username' => 'blog',
            'password' => 'xxxxxxxx',
        ),
    ),  
); 
例 DBクラス
$query = DB::select('user_id','exp')
    ->from('users')
    ->where('user_id', $user_id)
    ->execute('slave') // 引数で指定できる
    ->as_array();
例 ORM
$query = Model_User::find()
    ->where('user_id', $user_id)
    ->get(); // でっできないっっ

それを可能にするには以下のようにします。 

packages/orm/classes/query.phpを拡張するためapp以下にコピー
mkdir -p app/classes/core/orm/
cp ../packages/orm/classes/query.php app/classes/core/orm/
app/classes/core/orm/query.phpに以下のメソッドを追加
+
+       /**
+        * Change Connection
+        *
+        * @param   String connection
+        * @return  Query
+        */
+       public function connection($connection='default')
+       {
+               $this->connection = $connection;
+               return $this;
+       }
packages/orm/classes/bootstrap.phpを以下のように書き換え
-       'OrmQuery'        => __DIR__.'/classes/query.php'
+       'OrmQuery'        => APPPATH.'/classes/core/orm/query.php',
結果
$result = Model_User::find()
    ->where('user_id', $user_id)
    ->where('status', Model_User::STATUS_ACTIVE)
    ->connection('slave') // できたヽ(^o^)丿
    ->get_one();

これでormを使って複数DBの切り替えが出来るようになりました。

おまけ tasksをproductionモードでcronに設定する

fuel/app/tasks/batch.php をProductionモードでcronに設定する場合、

5 * * * * env FUEL_ENV=production /usr/local/bin/php oil r batch

開発環境と本番環境を区別させて実行するための必須設定ですね

以上!

あすは、@konkon1234さんの『(仮)FuelPHPで1サイトを作ってみて気が付いた点など』です。
お楽しみに!

FuelPHP1.3でRedisは注意

FuelPHP1.3でCacheのストレージをredisにするとあまりいいことが無いよってメモ。

詳しく調べてないけど、
fuel_xxxxxxx:index:とかいうkeyにkeyたちがserializeされた状態で保存されてて、
何か操作を行うたびにindexにkeyがあるかチェックするからkeyが増えるに従いトラフィックが増加する。

2012/11/05追記
1.3で1.2のredis使えば良いよ

1.2と1.3の差分
https://github.com/fuel/core/commit/11b6a890cbfcd7b4a5e98529b9d778dc2eacbdb3

もうちょい調査してみよう。

2012/11/12追記

どうやら1.2にはバグがあり正しくヒットしないことがあったが
1.3からは正しく動作するようになったからヒットするようになったらしい。

それにしてもCacheの仕組みがあまり良くないなぁ。
KVSにしてはいけない実装をしている気がする。

2012/11/17追記

なんかごちゃごちゃしてきたので整理しとこう。

Cacheクラスはストレージにfile,apc,memcached,redisが使える。

FuelPHP1.2のCacheクラスにはバグがあって正しくヒットしない。
FuelPHP1.3ではそのバグが修正されていて正しくヒットするようになった。
しかし、Cacheクラスのredisの実装が残念なためヒットはするが、トラフィックが増える。

有効期限を持たない永続的なデータを保存したい時は、
Redisクラスで直接保存したほうが良い。

そのほうがシンプルだし、Cacheクラスを使うとデータがFuelPHPに依存してしまうから、
よろしくないとおもいます。

Cacheクラス→ http://fuelphp.com/docs/classes/cache/usage.html
Redisクラス→http://fuelphp.com/docs/classes/redis.html

FuelPHP SessionでMemcacheを使う時の注意

Session Driverをmemcachedにしてlocalhost以外のサーバーに向けたい時は
Sessionクラス拡張しないといけないよ。

ってメモです。

■ core/configからsession.phpをapp/configにコピー

cp ../../core/config/session.php .  

■ driverをmemcachedに変更する

$ diff session.php ../../core/config/session.php
33c33
<       'driver'                        => 'memcached',
---
>       'driver'                        => 'cookie',

■ なぜか読み込まれたconfigをdumpしてみるとmemcachedのserversに2個設定がある

  array(2) {
    ["cookie_name"]=>
    string(7) "fuelmid"
    ["servers"]=>
    array(2) {
      [0]=>
      array(3) {
        ["host"]=>
        string(9) "127.0.0.1"
        ["port"]=>
        int(11211)
        ["weight"]=>
        int(100)
      }
      [1]=>
      array(3) {
        ["host"]=>
        string(9) "127.0.0.1"
        ["port"]=>
        int(11211)
        ["weight"]=>
        int(100)
      }
    }
  }

■ core/config/session.phpに書いてあるのserversの情報がマージされてしまうみたい

array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100)

■ session保存先を外のmemcacheサーバーに向けたい時にどうしよう

■ config.phpは拡張できない。
コア拡張の例外
ここにある通りconfigは拡張できません。
試しに拡張してみると上手く設定が読み込まれませんでした。(自分のやり方が変だった?)

■ 解決案1 本家でcore/config/session.phpのserversの設定を消してもらう。
session.phpのserversで127.0.0.1の設定無ければcore/classes/session/memcached.php内で
勝手に同じ情報がデフォルト値として設定されるので要らない。
自身でcoreの中を書き換えてしまうとバージョンアップ時にトラブルの元になるので避けたい。

■ 解決案2 core/classes/session.php拡張して_initメソッドだけ書き換える。

Config::loadの第4引数をtrueにすることで設定がOverwriteされるようになりcoreの設定が引き継がれなくなる。

session.phpを拡張
fuel/app/classes/session.php

<?php
class Session extends FuelCoreSession
{
    public static function _init()
    {
        //Config::load('session', true);
        Config::load('session', true, false, true);

        if (Config::get('session.auto_initialize', true))
        {
            static::instance();
        }
    }
}

Autoloaderに追加
fuel/app/bootstrap.php

Autoloader::add_classes(array(
    // Add classes you want to override here
    // Example: 'View' => APPPATH.'classes/view.php',
    'Session' => APPPATH.'classes/session.php',
));

解決案2がいいかなーと思います。