SQL Explain panel for the DebugKit plugin.
2009/3/20追記
微妙にバージョンアップしました。
http://d.hatena.ne.jp/cakephper/20090320/1237520921
2009/5/16追記
使い方の注意点が下記にあります。あつさんありがとう。layout指定は必須とか、htmlのheadタグは小文字で書くとか。
CakePHPクッキング「Explain SQL Component for Debugkitの注意点」
CakePHP 1.2.1を利用してます。
前に作ったSQL Explainコンポーネントを、DebugKitに組み込みました。前のコンポーネントはコンポーネント内にViewの仕事であるhtml生成などが入っていたため、bakeryに投稿した記事に対して、コンセプトはいいけど、Bad Componentだと言われました(coreコードのdbo_source::showLogメソッドとかどうなんよ?と思ったけど)
こいうものは、プラグイン化して、ちゃんとMVCを分けた方が良いとのことで、どうせならとDebugKitに組み込みました。DebugKitに組み込むのはすごく簡単で、感動しました。
ソースコードはここからダウンロードするか、thechawのプロジェクトから下記コマンドのようにgit cloneで落としてください。
git clone git@thechaw.com:forks/ichikaway/debug_kit.git
(追記:chawからgit cloneする場合は、事前にchawにアカウントを作ってpublic keyを登録しておく必要があります)
このDebugKitを入れると、下記のような画面になり、パネルがひとつ増えて、Explain結果を表示してくれます。(こっちの方が見たいときだけ見れるので便利)
設置は、app/plugins/debug_kitに一式置いてください。
そして、app_controller.phpで下記のようにコンポーネントを読み込んでください。
<?php class AppController extends Controller { var $components = array('DebugKit.Toolbar'); } ?>
これだけです。便利!
下記、Debug Kitへの組み込み方法を含めての解説です
まず、plugin/debugkit/controllers/components/toolbar.phpの下記の行を編集します。
var $_defaultPanels = array('history', 'session', 'request', 'sqlLog', 'timer', 'log', 'variables','sqlExplain');
上記のように、最後にsqlExplainという値を配列に追加します。
これによって、sqlExplainPanelクラスが自動で読み込まれます。
各Panelクラスは、plugin/debugkit/controllers/components/toolbar.phpに記述していくスタイルのようなので、同じようにこのファイルの最後に、下記のクラスを記述します。
<?php class sqlExplainPanel extends DebugPanel { var $plugin = 'debug_kit'; var $dbConfigs = array(); var $slowQueryThreshold = 0; /** * get db configs. * * @param string $controller * @access public * @return void */ function startUp(&$controller) { if (!class_exists('ConnectionManager')) { $this->dbConfigs = array(); return false; } $this->dbConfigs = ConnectionManager::sourceList(); return true; } /** * Get Sql Explain results for each DB config * * @param string $controller * @access public * @return array */ function beforeRender(&$controller) { $queryLogs = array(); if (!class_exists('ConnectionManager')) { return array(); } $count=1; foreach ( $this->dbConfigs as $configName ) { $db =& ConnectionManager::getDataSource( $configName ); if( empty($db->_queriesLog[0]) ){ continue; } $driver = $db->config['driver']; if( $driver === 'mysql' || $driver === 'postgres' ){ $explain_results['sqlexplain_driver'] = $driver; foreach( $db->_queriesLog as $key => $value ){ if( preg_match( '/^SELECT /i', $value['query'] ) && $value['took'] >= $this->slowQueryThreshold ){ $reesults = null; $results = $db->query( "Explain ". $value['query'] ); if( $driver === 'postgres' ){ //merge QIERY PLAN value $query_plan = array(); foreach( $results as $postgre_value ){ $query_plan[] = $postgre_value[0]['QUERY PLAN'] ; } $results[0][0]['QUERY PLAN'] = $query_plan; //change column order $results[0][0] = array_merge( array("id" => $count), $results[0][0] ); } $results[0][0]['query'] = $value['query']; $results[0][0]['id'] = $count; $explain_results[] = $results[0][0]; $count++; } } } } return $explain_results; } } ?>
各パネルのクラスは、beforeRenderに処理を記載して、debugkitがそれを自動で呼び出しています。
beforeRenderでreturnをすると、Viewでは$contentという変数にreturn値が入ります。今回は配列をreturnしました。
そして、Viewは下記のファイルを追加します。基本的に1パネル1Viewファイルです。
app/plugins/debug_kit/views/elements/sql_explain_panel.ctp
<h2><?php __('Sql Explain Results')?></h2> <?php if (!empty($content)) : ?> <?php $driver = $content['sqlexplain_driver']; unset($content['sqlexplain_driver']); ?> <div class="cake-sql-log"> <?php if( $driver === 'mysql' || $driver === 'postgres' ): ?> <?php $headers = array_keys($content[0]); foreach( $content as $rownum => $value ){ foreach( $value as $title => $linevalue ){ if( is_array($linevalue) ){ $linevalue_li = "<ul>"; foreach( $linevalue as $num => $arr_val ){ $linevalue_li .= "<li>"; $linevalue_li .= $arr_val; $linevalue_li .= "</li>"; } $linevalue_li .= "</ul>"; $linevalue = $linevalue_li; } $row[$rownum][] = $linevalue; } } echo $toolbar->table($row, $headers, array('title' => 'SQL Explain Results')); ?> <?php else: ?> <p><?php __('support only MySQL and PostgreSQL.'); ?></p> <?php endif; ?> </div> <?php else: ?> <p class="warning"><?php __('No active database connections'); ?></p> <?php endif; ?>|
$contentをコンポーネントから受け取ってるので、あとはその配列をテーブルタグに落とし込んでいるだけです。
テーブル作成は、$toolbar->tableメソッドで簡単にできます。第1引数にテーブルの内容の配列、第2引数にテーブルのヘッダ、第3引数にテーブルのタイトルを取ります。
テーブルの内容とヘッダの配列は、キーを数値にしているようなので、連想配列の文字列のキー名を変換しています。PostgreSQLだけは配列構造は一つ深くなるので、is_arrayで認識して配列をリストタグに入れるようにしています。
基本的にはこれだけです。簡単、便利!