この記事はBEAR.Sunday Advent Calendar 3日目の記事です。
はじめに
BEAR.Sundayは他のフレームワークと違って、データベースアクセスを他のモジュールと双方向に密結合させ利便性を追求していません。データベースアクセスはフレームワーク本体や他のコンポーネントと明確に切り離されています。
ORMやクエリービルダーを使っても、SQLファイルを使っても他のコンポーネントはそこに関心をもちません。
しかし、SQLをsql/ディレクトリに集約し、第一級市民として扱うアプローチを中心的な戦略として採用してきました。
- Koriym.QueryLocator - SQLファイルをIDで指定+count()の生成
- Ray.QueryModule - SQL実行を無名関数と束縛
- Ray.MediaQuery - SQL実行をPHPインターフェイスと束縛
便利なORMが全盛の現代において、SQLをわざわざ個別のファイルとして書き出すアプローチは、一見すると「冗長(Redundancy)」に映るかもしれません。
しかし、そこに**「明示性(Explicitness)」**という視点を持ち込むと、見えている景色がガラリと変わります。
その「記述の手間」は、無駄なコストではなく、未来の自分やチーム、そしてAIのための「正確なドキュメント」になります。
この記事ではSQLを隠蔽せず、あえて「第一級市民」として扱うことの価値について記します。
1. インラインスタイルと外部CSSの関係に似ている
コードの中にSQLを埋め込む行為(QueryBuilderやヒアドキュメント)と、外部ファイルに切り出す行為の違いは、Web開発におけるCSSの扱いで考えると腹落ちしやすいかもしれません。
-
埋め込みSQLは、HTMLの
style="color: red;"(インラインスタイル)のようなものです。- その場での実装は手軽ですが、アプリケーション全体でクエリの意図を把握したり、一括で管理したりすることは困難です。
-
SQLファイルは、
\<link rel="stylesheet"\>で読み込む外部CSSファイルです。- sql/ ディレクトリを見れば、アプリケーションがデータベースに対して「何をしようとしているか」が、カタログのように一目でわかります。
PHPのロジックを読まなくても、フォルダ内のファイル名(=ユースケース)を見るだけで、そのドメインのデータアクセスがわかります。
2. IDEのエコシステムを「味方」につける
SQLをPHPの文字列として扱っている限り、それはあくまで「文字列」に過ぎません。しかし、.sql という拡張子を持つファイルとして保存された瞬間、SQLは「第一級市民」となり、IDE(Navicat, DataGrip等)の強力な支援をネイティブに受けられるようになります。
- 完全な静的解析: 構文エラーやテーブルの存在確認が、実行前に確定します。
- 即時実行と検証: アプリを起動せずとも、エディタ上で直接クエリを実行し、EXPLAINでコストを確認できます。
この「安心感」と「試行錯誤の速さ」は、埋め込みSQLでは得られない体験です。
高機能なSQL IDEではクエリーの結果をCSVやJSONに出力する機能もあります。
裏を返せば、SQLファイルとして管理しなければ、これらの現代的なIDEによる強力な支援を享受できないことを意味します。
3. AIやツールとの「対話」がスムーズになる
テキストファイルとして独立したSQLは、人間だけでなく、マシン(AIやツール)にとっても極めて「読みやすい」存在になります。
CIによる品質の自動担保
例えば、Koriym.SqlQuality のようなツールを使えば、コミットのたびに以下のようなチェックを自動で行えます。
- 実行計画の診断: フルテーブルスキャンや、インデックスが無効化されている箇所を検出。
- Optimizer Impact: オプティマイザの補正がない状態での「素のクエリ性能」を評価。
AIエージェントとの協業
AIにパフォーマンスチューニングを依頼する際も、複雑なPHPクラス全体を解析させる必要はありません。「このSQLファイル」と「DDL」を渡すだけで済みます。コンテキストが純粋であるため、精度の高い改善案を引き出すことができます。
これまでの「遅いDBアクセス」を修正するアプローチ、つまりページ表示が実際に遅くなってから原因をさくぐる=病気にかかってから原因をさぐるような対症療法的なアプローチではなく、品質を未然に担保する防御的な開発が可能になります。
よくある質問(FAQ)
Q. SQLファイルとPHPファイルを行き来するのが面倒ではありませんか?
A. IDEの支援があれば、そのコストはほぼゼロです。
定義元へのジャンプ機能で一瞬で移動できます。むしろ、PHPコードの中に長大なSQL文字列やクエリ構築ロジックがないため、「ビジネスロジックを読む」際の認知負荷(Cognitive Load)は劇的に下がります。
Q. 検索条件によってWHERE句が変わるような、動的なクエリはどうするのですか?
A. SQLの工夫で吸収するか、責務を分けます。
多くの場合、column = :val OR :val IS NULL のようなイディオム(慣用句)や、CASE 式などを活用することで、条件分岐をSQL内に閉じ込めることが可能です。
\-- :name がある時は名前で検索、NULLの時は条件を無視(全件)
SELECT \* FROM users WHERE (name \= :name OR :name IS NULL);
それでも複雑になりすぎる場合は、「一つのクエリで何でもやろうとしすぎている」サインです。別のファイル(別のユースケース)として切り出すほうが、意図が「明示的」になり、テストも容易になります。
これはよくある質問ですが、実際はかなり少ない事が多いです。本当に必要な時にクエリービルダーを使えばいいのではないでしょうか。
Q. 生のSQLを書くなんて、時代に逆行していませんか?
A. それは「螺旋的発展」だと捉えています。
かつてSQLを直接書いていた時代とは違い、現代には静的解析、強力なIDE、CI、そしてAIがあります。これらを組み合わせることで、SQLは「管理不能な文字列」から「明示的でテスト可能なコード」へと進化しました。一周回って、より高いレベルに到達したのではないでしょうか。
結び
"Explicit is better than implicit."
(明示的な記述は、暗黙的な記述に勝る) - The Zen of Python
SQLをファイルに書くことは単なる作業量の増加といったコストではなく、データベース操作というPHPと違うパラダイムの操作を、アプリケーションが制御可能な「明示的な契約」へと昇華させるための、前向きな投資です。

