mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-03 13:58:55 +08:00
Merge pull request #1697 from Ragazzo/debug_module_db_panel_improvements
improved database panel in debug module
This commit is contained in:
@ -29,7 +29,7 @@ class Exact extends Base
|
||||
if (!$this->partial) {
|
||||
return (mb_strtolower($this->value, 'utf8') == mb_strtolower($value, 'utf8'));
|
||||
} else {
|
||||
return (mb_strpos($value, $this->value) !== false);
|
||||
return (mb_strpos(mb_strtolower($value, 'utf8'), mb_strtolower($this->value,'utf8')) !== false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
75
extensions/yii/debug/models/search/Db.php
Normal file
75
extensions/yii/debug/models/search/Db.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace yii\debug\models\search;
|
||||
|
||||
use yii\data\ArrayDataProvider;
|
||||
use yii\debug\components\search\Filter;
|
||||
|
||||
/**
|
||||
* Db represents the model behind the search form about current request database queries.
|
||||
*/
|
||||
class Db extends Base
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string type attribute input search value
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var integer query attribute input search value
|
||||
*/
|
||||
public $query;
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
[['type', 'query'], 'safe'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function attributeLabels()
|
||||
{
|
||||
return [
|
||||
'type' => 'Type',
|
||||
'query' => 'Query',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data provider with filled models. Filter applied if needed.
|
||||
* @param array $params
|
||||
* @param array $models
|
||||
* @return \yii\data\ArrayDataProvider
|
||||
*/
|
||||
public function search($params, $models)
|
||||
{
|
||||
$dataProvider = new ArrayDataProvider([
|
||||
'allModels' => $models,
|
||||
'pagination' => [
|
||||
'pageSize' => 10,
|
||||
],
|
||||
'sort' => [
|
||||
'attributes' => ['duration','type','query'],
|
||||
'defaultOrder' => [
|
||||
'duration' => SORT_DESC,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
if (!($this->load($params) && $this->validate())) {
|
||||
return $dataProvider;
|
||||
}
|
||||
|
||||
$filter = new Filter();
|
||||
$this->addCondition($filter, 'type', true);
|
||||
$this->addCondition($filter, 'query', true);
|
||||
$dataProvider->allModels = $filter->filter($models);
|
||||
|
||||
return $dataProvider;
|
||||
}
|
||||
|
||||
}
|
||||
@ -7,10 +7,10 @@
|
||||
|
||||
namespace yii\debug\panels;
|
||||
|
||||
use Yii;
|
||||
use yii\debug\Panel;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\log\Logger;
|
||||
use yii\helpers\Html;
|
||||
use yii\debug\models\search\Db;
|
||||
|
||||
/**
|
||||
* Debugger panel that collects and displays database queries performed.
|
||||
@ -20,6 +20,17 @@ use yii\helpers\Html;
|
||||
*/
|
||||
class DbPanel extends Panel
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array db queries info extracted to array as models, to use with data provider.
|
||||
*/
|
||||
private $_models;
|
||||
|
||||
/**
|
||||
* @var array current database request timings
|
||||
*/
|
||||
private $_timings;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'Database';
|
||||
@ -29,62 +40,28 @@ class DbPanel extends Panel
|
||||
{
|
||||
$timings = $this->calculateTimings();
|
||||
$queryCount = count($timings);
|
||||
$queryTime = 0;
|
||||
foreach ($timings as $timing) {
|
||||
$queryTime += $timing[3];
|
||||
}
|
||||
$queryTime = number_format($queryTime * 1000) . ' ms';
|
||||
$url = $this->getUrl();
|
||||
$output = <<<EOD
|
||||
<div class="yii-debug-toolbar-block">
|
||||
<a href="$url" title="Executed $queryCount database queries which took $queryTime.">
|
||||
DB <span class="label">$queryCount</span> <span class="label">$queryTime</span>
|
||||
</a>
|
||||
</div>
|
||||
EOD;
|
||||
return $queryCount > 0 ? $output : '';
|
||||
$queryTime = number_format($this->getTotalQueryTime($timings) * 1000) . ' ms';
|
||||
|
||||
return Yii::$app->view->render('panels/db/summary',[
|
||||
'timings' => $this->calculateTimings(),
|
||||
'panel' => $this,
|
||||
'queryCount' => $queryCount,
|
||||
'queryTime' => $queryTime,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getDetail()
|
||||
{
|
||||
$timings = $this->calculateTimings();
|
||||
ArrayHelper::multisort($timings, 3, SORT_DESC);
|
||||
$rows = [];
|
||||
foreach ($timings as $timing) {
|
||||
$duration = sprintf('%.1f ms', $timing[3] * 1000);
|
||||
$procedure = Html::encode($timing[1]);
|
||||
$traces = $timing[4];
|
||||
if (!empty($traces)) {
|
||||
$procedure .= Html::ul($traces, [
|
||||
'class' => 'trace',
|
||||
'item' => function ($trace) {
|
||||
return "<li>{$trace['file']}({$trace['line']})</li>";
|
||||
},
|
||||
]);
|
||||
}
|
||||
$rows[] = "<tr><td style=\"width: 80px;\">$duration</td><td>$procedure</td>";
|
||||
}
|
||||
$rows = implode("\n", $rows);
|
||||
$searchModel = new Db();
|
||||
$dataProvider = $searchModel->search($_GET, $this->getModels());
|
||||
|
||||
return <<<EOD
|
||||
<h1>Database Queries</h1>
|
||||
|
||||
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 80px;">Time</th>
|
||||
<th>Query</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
$rows
|
||||
</tbody>
|
||||
</table>
|
||||
EOD;
|
||||
return Yii::$app->view->render('panels/db/detail',[
|
||||
'panel' => $this,
|
||||
'dataProvider' => $dataProvider,
|
||||
'searchModel' => $searchModel,
|
||||
]);
|
||||
}
|
||||
|
||||
private $_timings;
|
||||
|
||||
protected function calculateTimings()
|
||||
{
|
||||
if ($this->_timings !== null) {
|
||||
@ -120,4 +97,57 @@ EOD;
|
||||
$messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, ['yii\db\Command::query', 'yii\db\Command::execute']);
|
||||
return ['messages' => $messages];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns total queries time.
|
||||
* @param array $timings
|
||||
* @return integer total time
|
||||
*/
|
||||
protected function getTotalQueryTime($timings)
|
||||
{
|
||||
$queryTime = 0;
|
||||
|
||||
foreach ($timings as $timing) {
|
||||
$queryTime += $timing[3];
|
||||
}
|
||||
|
||||
return $queryTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of models that represents logs of the current request. Can be used with data providers,
|
||||
* like yii\data\ArrayDataProvider.
|
||||
* @return array models
|
||||
*/
|
||||
protected function getModels()
|
||||
{
|
||||
if ($this->_models === null || $refresh) {
|
||||
$this->_models = [];
|
||||
$timings = $this->calculateTimings();
|
||||
|
||||
foreach($timings as $dbTiming) {
|
||||
$this->_models[] = [
|
||||
'type' => $this->detectQueryType($dbTiming[1]),
|
||||
'query' => $dbTiming[1],
|
||||
'duration' => ($dbTiming[3] * 1000), #in milliseconds
|
||||
'trace' => $dbTiming[4],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->_models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects databse timing type. Detecting is produced through simple parsing to the first space|tab|new row.
|
||||
* First word before space is timing type. If there is no such words, timing will have empty type.
|
||||
* @param string $timing timing procedure string
|
||||
* @return string query type select|insert|delete|etc
|
||||
*/
|
||||
protected function detectQueryType($timing)
|
||||
{
|
||||
$timing = ltrim($timing);
|
||||
preg_match('/^([a-zA-z]*)/', $timing, $matches);
|
||||
return count($matches) ? $matches[0] : '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ class LogPanel extends Panel
|
||||
/**
|
||||
* @var array log messages extracted to array as models, to use with data provider.
|
||||
*/
|
||||
private $_models ;
|
||||
private $_models;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
|
||||
53
extensions/yii/debug/views/default/panels/db/detail.php
Normal file
53
extensions/yii/debug/views/default/panels/db/detail.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
use yii\helpers\Html;
|
||||
use yii\grid\GridView;
|
||||
?>
|
||||
<h1>Database Queries</h1>
|
||||
|
||||
<?php
|
||||
|
||||
echo GridView::widget([
|
||||
'dataProvider' => $dataProvider,
|
||||
'id' => 'db-panel-detailed-grid',
|
||||
'filterModel' => $searchModel,
|
||||
'filterUrl' => $panel->getUrl(),
|
||||
'columns' => [
|
||||
['class' => 'yii\grid\SerialColumn'],
|
||||
[
|
||||
'attribute' => 'duration',
|
||||
'value' => function ($data)
|
||||
{
|
||||
return sprintf('%.1f ms',$data['duration']);
|
||||
},
|
||||
],
|
||||
[
|
||||
'attribute' => 'type',
|
||||
'value' => function ($data)
|
||||
{
|
||||
return Html::encode(mb_strtoupper($data['type'],'utf8'));
|
||||
},
|
||||
],
|
||||
[
|
||||
'attribute' => 'query',
|
||||
'value' => function ($data)
|
||||
{
|
||||
$query = Html::encode($data['query']);
|
||||
|
||||
if (!empty($data['trace'])) {
|
||||
$query .= Html::ul($data['trace'], [
|
||||
'class' => 'trace',
|
||||
'item' => function ($trace) {
|
||||
return "<li>{$trace['file']}({$trace['line']})</li>";
|
||||
},
|
||||
]);
|
||||
}
|
||||
return $query;
|
||||
},
|
||||
'format' => 'html',
|
||||
'options' => [
|
||||
'width' => '70%',
|
||||
],
|
||||
]
|
||||
],
|
||||
]);
|
||||
?>
|
||||
7
extensions/yii/debug/views/default/panels/db/summary.php
Normal file
7
extensions/yii/debug/views/default/panels/db/summary.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php if ($queryCount): ?>
|
||||
<div class="yii-debug-toolbar-block">
|
||||
<a href="$url" title="Executed <?php echo $queryCount; ?> database queries which took <?php echo $queryTime; ?>.">
|
||||
DB <span class="label"><?php echo $queryCount; ?></span> <span class="label"><?php echo $queryTime; ?></span>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
Reference in New Issue
Block a user