[CakePHP]ConnectionManagerを置き換えてプラグイン内のデータソースも読み込む

  • strict warning: Non-static method view::load() should not be called statically in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/views.module on line 842.
  • strict warning: Declaration of views_handler_argument::init() should be compatible with views_handler::init(&$view, $options) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/handlers/views_handler_argument.inc on line 745.
  • strict warning: Declaration of views_plugin_row::options_validate() should be compatible with views_plugin::options_validate(&$form, &$form_state) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/plugins/views_plugin_row.inc on line 135.
  • strict warning: Declaration of views_plugin_row::options_submit() should be compatible with views_plugin::options_submit(&$form, &$form_state) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/plugins/views_plugin_row.inc on line 135.
  • strict warning: Non-static method view::load() should not be called statically in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/views.module on line 842.
  • strict warning: Declaration of views_handler_filter::options_validate() should be compatible with views_handler::options_validate($form, &$form_state) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/handlers/views_handler_filter.inc on line 589.
  • strict warning: Declaration of views_handler_filter::options_submit() should be compatible with views_handler::options_submit($form, &$form_state) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/handlers/views_handler_filter.inc on line 589.
  • strict warning: Declaration of views_handler_filter_boolean_operator::value_validate() should be compatible with views_handler_filter::value_validate($form, &$form_state) in /home/lyniqne/public_html/planetcakephp.org/drupal/sites/all/modules/views/handlers/views_handler_filter_boolean_operator.inc on line 149.

なんとタイムリーな…需要ありまくりですw

こんな新米ブログに言及していただけるとわ…

http://www.1x1.jp/blog/2009/05/cakephp_app_import_swap_framework.html

これはさっそく置き換えてみないわけにはいくまいというわけで

マイConnectionManagerの置き場所は迷ったのですが、せっかくなので(?)外部プラグインのtools/modelsに入れておくことにしました。

bootstrap.php

<?php
$pluginPaths = array(CAKE_CORE_INCLUDE_PATH . DS . 'plugins' . DS);
App::import('Core', 'ConnectionManager', 
    array('file' => $pluginPaths[0] . 'tools' . DS . 'models' . DS . 'connection_manager.php'));
?>

やりたいこと

  • plugins/models/datasourcesをデータソースの検索対象にする。
  • プラグイン内データソースの指定は、"プラグイン名.データソース名"にしたい。
  • 頭文字は大文字使えた方が格好よくない?*1

$pluginPathsを取得するには

bootstrap.phpで定義した*2pluginPaths等はConfigureインスタンスが持っている模様。

以下のようにして取得できる。

<?php
$conf = Configure::getInstance();
$pluginPaths = $conf->pluginPaths;
?>

データベースコンフィグを定義

database.php

<?php
var $hoge = array('datasource' => 'Tools.Hoge'); 
?>

この場合のパスはplugins/tools/models/datasources/hoge_source.php

それじゃあConnentionManagerを書き換えよう

とはいえあまり多くの修正はしたくない。

今回は幸いloadDataSourceと__getDriverの修正だけで済みました。

connection_manager.php

<?php
    function loadDataSource($connName) {
        $_this =& ConnectionManager::getInstance();
        if (is_array($connName)) {
            $conn = $connName;
        } else {
            $connections = $_this->enumConnectionObjects();
            $conn = $connections[$connName];
        }
        if (!empty($conn['parent'])) {
            $_this->loadDataSource($conn['parent']);
        }
        if (class_exists($conn['classname'])) {
            return false;
        }
        
        // プラグイン名が指定されてたらpluginPathsを検索する
        if (!empty($conn['plugin'])) {
            if (!$this->__loadDataSourceInPluginPaths($conn['plugin'], $conn['filename'])) {
                $error = __('Unable to load DataSource file %s.php in %s plugin.', true);
                trigger_error(sprintf($error, $conn['filename'], $conn['plugin']), E_USER_ERROR);
                return null;
            }
        } elseif (file_exists(MODELS . 'datasources' . DS . $conn['filename'] . '.php')) {
            require (MODELS . 'datasources' . DS . $conn['filename'] . '.php');
        } elseif (fileExistsInPath(LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php')) {
            require (LIBS . 'model' . DS . 'datasources' . DS . $conn['filename'] . '.php');
        } else {
            $error = __('Unable to load DataSource file %s.php', true);
            trigger_error(sprintf($error, $conn['filename']), E_USER_ERROR);
            return null;
        }
    }
    
    // pluginPathsを検索、あればデータソースをrequireする
    function __loadDataSourceInPluginPaths($plugin, $filename)
    {
        $conf = Configure::getInstance();
        foreach ($conf->pluginPaths as $path) {
            $folder = new Folder($path);
            list($plugins, $files) = $folder->read();
            if (in_array($plugin, $plugins)) {
                if (file_exists($path . $plugin . DS . 'models' . DS . 'datasources' . DS . $filename . '.php')) {
                    require ($path . $plugin . DS . 'models' . DS . 'datasources' . DS . $filename . '.php');
                    return true;
                }
            }
        }
        return false;
    }
    
    function __getDriver($config) {
        $plugin = null;
        if (!isset($config['datasource'])) {
            $config['datasource'] = 'dbo';
        } else if (strstr($config['datasource'], '.')){
      // .区切りでプラグイン名を抽出
            list($plugin, $config['datasource']) = explode('.', $config['datasource']);
            $plugin = low($plugin); // プラグイン名が大文字で指定されているかもしれないので小文字に
        }
        $config['datasource'] = low($config['datasource']); // 同じくデータソース名も小文字に
        
        if (isset($config['driver']) && $config['driver'] != null && !empty($config['driver'])) {
            $filename = $config['datasource'] . DS . $config['datasource'] . '_' . $config['driver'];
            $classname = Inflector::camelize(strtolower($config['datasource'] . '_' . $config['driver']));
            $parent = $this->__getDriver(array('datasource' => $config['datasource']));
        } else {
            $filename = $config['datasource'] . '_source';
            $classname = Inflector::camelize(strtolower($config['datasource'] . '_source'));
            $parent = null;
        }
        // プラグイン名を付加して返す
        return array('filename'  => $filename, 'classname' => $classname, 'parent' => $parent, 'plugin' => $plugin);
    }
?>

appがきれいさっぱり

これでデータソースも外部プラグインに追いやれました。

もはやappには何も無い…

これが無の境地か

bootstrapの設定とかは

コンフィグ系などアプリごとにファイルを持たなければいけなかったり、

設定する必要があるものに関してはシェルを作ってそこからインストールするようにすれば楽ちん。

アップデート時の更新し忘れも防げる。

しかもシェルのcreateFileメソッドはちゃんと上書き確認もしてくれる(はず)。*3

でも実はまだ1つ悩み中

AppModelの上書きメソッドをどうするか*4

*1:ただし頭文字以外も大文字に出来てしまう件

*2:+appデフォルトのプラグインパスが入っている

*3:逆にうざい時も

*4:alphaNumericとかalphaNumericとかalphaNumericとか