[ Japanese | English ]
Laqu is Laravel Db Query Helper.
- 実行されたdbクエリを確認できます
- PHPUnit でのアサーション、実行時間による並べ替え、ビルド後のクエリのチェックなど
このライブラリは、開発中に利用されることを想定しています。
【Laravel】実行されたDBクエリの確認ができるやつを書いた - Qiita
- PHP 7.4+ or newer(7.4, 8.0)
- Laravel
6.x
,7.x
,8.x
Via composer.
$ composer require --dev shimabox/laqu
Develop.
$ git clone https://github.com/shimabox/laqu.git
$ cd laqu
$ composer install
期待するクエリが流れているかPHPUnitでアサーションするためのものです。
traitです。assertQuery()を使います。
<?php
use App\Repositories\ExampleRepository; // example.
use Laqu\QueryAssertion;
use Tests\TestCase;
class QueryAssertionTest extends TestCase
{
use QueryAssertion;
private $exampleRepository;
protected function setUp(): void
{
parent::setUp();
$this->exampleRepository = new ExampleRepository();
}
public function queryTest()
{
// 基本的な使い方
$this->assertQuery(
// クエリが実行される処理をクロージャに渡します
fn () => $this->exampleRepository->findById('a123'),
// 期待するクエリを書きます
'select from user where id = ? and is_active = ?',
// バインドされる値を配列で定義します
// (bindするものがない場合は空配列を渡すか、引数は渡さないでOK)
[
'a123',
1,
]
);
// 複数のクエリを確認
// 基本的には 1メソッド1クエリの確認 を推奨しますが、中には1メソッドで複数クエリが流れる場合もあると思います。
// その場合は下記のようにクエリとバインド値を配列で対になるように定義してください。
$this->assertQuery(
// 例えばこの処理で複数のクエリが流れるとします
fn () => $this->exampleRepository->findAll(),
// 期待するクエリをそれぞれ配列で定義します
[
'select from user where is_active = ?', // ※1
'select from admin_user where id = ? and is_active = ?', // ※2
'select from something', // ※3
],
// バインドされる値を二次元配列で定義(bindするものがない場合は空配列を渡してください)
[
[ // ※1.
1,
],
[ // ※2.
'b123',
1,
],
// ※3 はバインド無しの想定なので空配列を渡します
[],
]
);
}
}
クエリが流れるメソッドを渡して、どのようなクエリが流れたのか確認することができます。
QueryAnalyzer::analyze()
で実行されたクエリの結果(Laqu\Analyzer\QueryList
)が取得できます。
※ QueryListはLaqu\Analyzer\Query
を持ちます
<?php
use Laqu\Facades\QueryAnalyzer;
/** @var Laqu\Analyzer\QueryList **/
$analyzed = QueryAnalyzer::analyze(function () {
$author = Author::find(1);
$author->delete();
});
/*
Laqu\Analyzer\QueryList {#345
-queries: array:2 [
0 => Laqu\Analyzer\Query {#344
-query: "select * from "authors" where "authors"."id" = ? limit 1"
-bindings: array:1 [
0 => 1
]
-time: 0.08
-buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1"
}
1 => Laqu\Analyzer\Query {#337
-query: "delete from "authors" where "id" = ?"
-bindings: array:1 [
0 => "1"
]
-time: 0.03
-buildedQuery: "delete from "authors" where "id" = '1'"
}
]
}
*/
dump($analyzed);
// select * from "authors" where "authors"."id" = 1 limit 1
echo $analyzed[0]->getBuildedQuery();
// delete from "authors" where "id" = '1'
echo $analyzed[1]->getBuildedQuery();
/*
Laqu\Analyzer\Query {#337
-query: "delete from "authors" where "id" = ?"
-bindings: array:1 [
0 => "1"
]
-time: 0.03
-buildedQuery: "delete from "authors" where "id" = '1'"
}
*/
dump($analyzed->extractFastestQuery());
/*
Laqu\Analyzer\Query {#344
-query: "select * from "authors" where "authors"."id" = ? limit 1"
-bindings: array:1 [
0 => 1
]
-time: 0.08
-buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1"
}
*/
dump($analyzed->extractSlowestQuery());
/*
array:2 [
0 => Laqu\Analyzer\Query {#337
-query: "delete from "authors" where "id" = ?"
-bindings: array:1 [
0 => "1"
]
-time: 0.03
-buildedQuery: "delete from "authors" where "id" = '1'"
}
1 => Laqu\Analyzer\Query {#344
-query: "select * from "authors" where "authors"."id" = ? limit 1"
-bindings: array:1 [
0 => 1
]
-time: 0.08
-buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1"
}
]
*/
dump($analyzed->sortByFast());
/*
array:2 [
0 => Laqu\Analyzer\Query {#344
-query: "select * from "authors" where "authors"."id" = ? limit 1"
-bindings: array:1 [
0 => 1
]
-time: 0.08
-buildedQuery: "select * from "authors" where "authors"."id" = 1 limit 1"
}
1 => Laqu\Analyzer\Query {#337
-query: "delete from "authors" where "id" = ?"
-bindings: array:1 [
0 => "1"
]
-time: 0.02
-buildedQuery: "delete from "authors" where "id" = '1'"
}
]
*/
dump($analyzed->sortBySlow());
QueryLogは Basic Database Usage - Laravel - The PHP Framework For Web Artisans の処理をラップしたものです。
※ 実行時間関連はそこまで精密ではありません
<?php
use Laqu\Facades\QueryLog;
$queryLog = QueryLog::getQueryLog(fn () => Author::find(1));
/*
array:1 [
0 => array:3 [
"query" => "select * from "authors" where "authors"."id" = ? limit 1"
"bindings" => array:1 [
0 => 1
]
"time" => 0.12
]
]
*/
dump($queryLog);
クエリとバインドパラメータを渡すと、実行されるクエリの確認ができます。
pdo-debug/pdo-debug.php at master · panique/pdo-debug を参考にしています。
<?php
use Laqu\Facades\QueryHelper;
$now = Carbon::now();
$from = $now->copy()->subDay();
$to = $now->copy()->addDay();
$query = 'select * from authors where id in (?, ?) and name like :name and updated_at between ? and ?';
$bindings = [
1,
2,
'%Shakespeare',
$from,
$to,
];
$buildedQuery = QueryHelper::buildedQuery($query, $bindings);
// select * from authors where id in (1, 2) and name like '%Shakespeare' and updated_at between '2020-07-07 00:37:55' and '2020-07-09 00:37:55'
echo $buildedQuery;
QueryFormatterはDoctrine\SqlFormatter\SqlFormatter
のラッパーです。
@see doctrine/sql-formatter: A lightweight php class for formatting sql statements. Handles automatic indentation and syntax highlighting.
デフォルトはNullHighlighter
を利用していますが、CLI、HTMLでのフォーマットも可能です。
デフォルトのHighlighterはDoctrine\SqlFormatter\NullHighlighter
です。
<?php
use Laqu\Facades\QueryFormatter;
$query = "SELECT count(*),`Column1`,`Testing`, `Testing Three` FROM `Table1`
WHERE Column1 = 'testing' AND ( (`Column2` = `Column3` OR Column4 >= NOW()) )
GROUP BY Column1 ORDER BY Column3 DESC LIMIT 5,10";
/*
SELECT
count(*),
`Column1`,
`Testing`,
`Testing Three`
FROM
`Table1`
WHERE
Column1 = 'testing'
AND (
(
`Column2` = `Column3`
OR Column4 >= NOW()
)
)
GROUP BY
Column1
ORDER BY
Column3 DESC
LIMIT
5, 10
*/
echo QueryFormatter::format($query);
Doctrine\SqlFormatter\CliHighlighter
を利用する場合は以下の通り、CliHighlighter
をインジェクトしてください。
<?php
use Doctrine\SqlFormatter\CliHighlighter;
use Laqu\Formatter\QueryFormatter;
/** @var QueryFormatter */
$formatter = app()->make(QueryFormatter::class/* or 'queryFormatter' */, [new CliHighlighter()]);
echo $formatter->format($query);
Doctrine\SqlFormatter\HtmlHighlighter
を利用する場合は以下の通り、HtmlHighlighter
をインジェクトしてください。
<?php
use Doctrine\SqlFormatter\HtmlHighlighter;
use Laqu\Formatter\QueryFormatter;
/** @var QueryFormatter */
$formatter = app()->make(QueryFormatter::class/* or 'queryFormatter' */, [new HtmlHighlighter()]);
echo $formatter->format($query);
使い方は format()
とほぼ同じです。
https://github.com/doctrine/sql-formatter#syntax-highlighting-only を参照してください。
<?php
use Laqu\Facades\QueryFormatter;
$query = '...';
echo QueryFormatter::highlight($query);
compress()
はすべてのコメントと余計な空白を取り除いたクエリを返却します。
https://github.com/doctrine/sql-formatter#compress-query を参照してください。
<?php
use Laqu\Facades\QueryFormatter;
$query = <<<SQL
-- This is a comment
SELECT
/* This is another comment
On more than one line */
Id #This is one final comment
as temp, DateCreated as Created FROM MyTable;
SQL;
// SELECT Id as temp, DateCreated as Created FROM MyTable;
echo QueryFormatter::compress($query);
check.
$ composer phpcs
fix.
$ composer phpcs:fix
check.
$ composer phpstan
$ composer test
Run all the above commands at once.
$ composer ci
テストで確認しているクエリのパターンがまだまだ少ないです。