cakePHPのuseDbConfigでのmodel接続先選択方法と cannot redeclare class dbsource が出る問題

cakePHPでSQLのread/writeで接続先のdbを変える方法が Load Balancing and MySQL Master and Slaves | The Bakery, Everything CakePHP : Articles に書いてあって、そりゃ簡単だけど毎回変更して終わったら元に戻すなんて美しくないなーと思いながら(コードからするとその度にstatしてたりするのでパフォーマンス上もよくない)、まねして書いてみると

Fatal error: Cannot redeclare class dbosource ..../cake/libs/model/connection_manager.php on line 170

って出てきて動きゃしねー。ちなみにバージョンは cake_1.1.14.4797 です。

問題のコードは

        if(file_exists(MODELS . $conn['filename'] . '.php')) {
            require (MODELS . $conn['filename'] . '.php');
        } else if (fileExistsInPath(LIBS . 'model' . DS . $conn['filename'] . '.php')) {
            require (LIBS . 'model' . DS . $conn['filename'] . '.php');
        } else {

なかんじなのでrequirerequire_onceにすればとりあえずはいけそうだと思いつつ、新しいのだと直ってるのかなとnightly見に行って1.2.xのソースをのぞいてみたけど問題の箇所は変わってない。

“Cannot redeclare class dbosource”で調べるとわりと出てきて、あたりだったのは newbie question about multiple databases で、スレッドの最後の方 Re: newbie question about multiple databases

Well, it turns out that it's a bug that first appeared in 1.1.14. It
seems that it was first reported on the 8th. The ticket number is 2370.

って書いてある。
チケット2370を見に行くと #2370 (dbo_source.php can be loaded more than once in Cake 1.1.14) – CakePHP : The Rapid Development Framework for PHP – Trac には2007/4/11にcloseって書いてある。でもの1.2.xのnightlyでは直ってないよー。1.1.xだったらなおってるのかなー。

そのへんはいいとしてticket#2370によれば単純にrequirerequire_onceにするのでokだそうです。

require_onceにしたら上の問題は解決したけど、やっぱりdb切り替わんない。
Load Balancing and MySQL Master and Slaves | The Bakery, Everything CakePHP : Articles をみたら

One More Thing
I feel like Columbo, but I forgot to mention that this does not work in cakePHP versions before 1.1.15

はいはい。了解です。

でもそもそも

    function save($data = null, $validate = true, $fieldList = array()) {
        $db =& ConnectionManager::getDataSource($this->useDbConfig);

        if ($data) {
            if (countdim($data) == 1) {
                $this->set(array($this->name => $data));
            } else {
                $this->set($data);
            }
        }

        $whitelist = !(empty($fieldList) || count($fieldList) == 0);

        if ($validate && !$this->validates()) {
            return false;
        }

        if (!$this->beforeSave()) {
            return false;
        }

こうなってて beforeSave が呼ばれる前に取得された$dbがSQL発行するときに使われてるから beforeSave$this->useDbConfig変更するんじゃ手遅れな気がするんだけど。
そもそもcake_1.1.15.5144でも Cannot redeclare class dbosource の原因になってる connection_manager.php の require_once にするべき部分は require のままになってて、動かなそうな気配もある…..

自分の手元のソースとかinclude pathとかがおかしいのか….(よくわかりません)

cake_1.1.15.5144もってきてやってみたらrequire問題は発生しないものの(自分が書いたコードだとひとつのコントローラで複数のモデルを使っているけど、1.1.15でテストで書いたコードはひとつのモデルしか使ってないからだと思われます)、やっぱりdbの接続先は変わらず。

けっきょく app_model.php で

    function save($data = null, $validate = true, $fieldList = array()) {
        $old = $this->useDbConfig;
        $this->useDbConfig = 'master';
        $ret = parent::save($data, $validate, $fieldList);
        $this->useDbConfig = $old;
        return $ret;
    }

    function del($id = null, $cascade = true) {
        $old = $this->useDbConfig;
        $this->useDbConfig = 'master';
        $ret = parent::del($id, $cascade);
        $this->useDbConfig = $old;
        return $ret;
    }

にした。

ぜんぜん美しくない。

追記 2008.1.24

beforeSave()の呼び出しタイミングについて – chinamiyabiの日記にもbeforeSave/afterSaveではうまくいかないという記述があった。バージョンは不明。


About this entry