生SQLの地獄から脱出!レガシーPHP改善の現実解「PORM」開発者が語る5年の戦い

生SQLの地獄から脱出!レガシーPHP改善の現実解「PORM」開発者が語る5年の戦い プログラミング

レガシーシステムのモダナイゼーション。
この言葉を聞いて、あなたはどんな感情を抱きますか?

多くの開発者が頭を抱える課題です。
特にPHPの世界では、10年以上前に作られたシステムが今も現役で動いています。

生のSQLが散在しています。
SQLインジェクションの脆弱性も抱えています。

さらに、型情報もなく、IDEの恩恵を受けられません。
そんなコードベースを前にして、途方に暮れた経験はありませんか?

最近、興味深いアプローチが登場しました。
オーストラリアの開発者が5年間の実戦経験から生み出した軽量ORM「PORM」です。

今回は、このツールの設計思想と実装について深掘りしてみましょう。

なぜ新しいORMが必要だったのか

「Doctrineを使えばいいじゃないか」

そう思うかもしれません。
確かにDoctrineは強力なORMです。

しかし、レガシープロジェクトには特有の制約があります。
まず、既存のコードベースへの影響を最小限に抑える必要があります。

PORMの開発者が携わっていたプロジェクトは特殊でした。
オーストラリアの特定業界で3分の2のシェアを持つシステムだったのです。
大規模な変更は許されません。

次に、段階的な移行が可能でなければなりません。
一気にすべてを書き換えるのは現実的ではないのです。

そして何より、開発者の学習コストを抑える必要があります。
チーム全員がDoctrineの複雑な仕組みを理解するには時間がかかりすぎます。

PORMの革新的なアプローチ

PORMは、これらの課題に対して独自の解決策を提示しています。

最小限の侵襲性

PORMが必要とするのはPDOStatementオブジェクトだけです。
既存のデータベース接続をそのまま活用できます。

グローバル変数として定義された$mysqlでも問題ありません。

$stmt = $pdo->prepare("SELECT * FROM users WHERE active = 1");
$stmt->execute();
$users = User::many($stmt);

シンプルではありませんか?
既存のSQL文をそのまま使えます。

そして、結果をオブジェクトとして扱えるようになります。

N+1問題への独自解決

ORMを使う際の大きな課題の一つが、N+1クエリ問題です。
PORMは「siblings」という独創的な概念でこれを解決しています。

記事を取得して著者名を表示する場合を考えてみましょう。
通常なら、記事の数だけ著者情報を取得するクエリが発生します。

しかし、PORMは違います。最初のアクセス時に、すべての記事の著者情報をまとめて取得するのです。

foreach ($posts as $post) {
    echo $post->author->name; // 最初のアクセスで全著者を取得
}

この仕組みは、Nette Frameworkからインスピレーションを受けたそうです。
実装は複雑です。

でも、使う側はシンプルに書けます。

ActiveRecordパターンの限定的採用

PORMはActiveRecordパターンを採用しています。

しかし、すべてをカバーしようとはしません。
複雑な書き込み操作では、生のSQLを書くことを推奨しています。

なぜでしょうか?
現実のビジネスロジックは、単一テーブルの操作では表現しきれないからです。

例えば複数テーブルを扱う操作を考えてみましょう。
この操作では複数のテーブルを更新します:

  • 在庫テーブル
  • 履歴テーブル
  • 価格テーブル

こういった場合、素直にSQLを書いたほうが明確です。

批判と議論

RedditでPORMが公開されたとき、様々な意見が寄せられました。

「静的なDBクラスは設計として問題がある」という指摘がありました。
確かに、グローバルな状態を持つことはテストの妨げになります。

しかし、開発者は説明しています。
DBクラスは単なる例であり、PORMの本質ではないと。

「AIの時代にORMは必要か?」という根本的な問いも投げかけられました。
確かにAIによるコード補完は進化しています。

でも、型安全性や構造化されたコードの価値は変わりません。

実装の詳細

PORMの実装には、いくつか興味深い工夫があります。

Jinja2風のテンプレート変数

DBクラスでは、PHPの__callStaticマジックメソッドを活用しています。
これにより、独特な仕組みを実現しています。

静的メソッドのように見えて、実はインスタンスメソッドを呼び出すのです。

DB::select("SELECT * FROM users WHERE id = :id", ['id' => 1]);

一見すると静的メソッドです。

しかし、内部ではシングルトンインスタンスのメソッドが呼ばれています。
賛否両論ありますが、レガシーコードとの親和性を重視した結果でしょう。

mysqli対応の可能性

興味深いことに、開発者は元々mysqliベースのプロジェクトでも使っていたと明かしています。

PDOへの依存は実は2つのメソッドだけ。
将来的にはmysqliもサポートする予定だそうです。

レガシーシステムと向き合う哲学

PORMの開発から学べることは多くあります。

完璧を求めすぎないこと
すべてをカバーする巨大なフレームワークより、特定の問題を解決する小さなツールのほうが価値があることもあります。

段階的な改善を重視すること
一気に理想の状態を目指すのではなく、少しずつ良くしていく。
この考え方がレガシーシステムの改善には不可欠です。

現実と向き合うこと
理想的な設計より、今あるコードベースで動くことのほうが重要な場合もあります。

まとめ

PORMは万能の解決策ではありません。
しかし、レガシーPHPプロジェクトに苦しむ開発者にとって、一つの選択肢を提供しています。

重要なのは、自分のプロジェクトに合ったツールを選ぶことです。
Doctrineが適切な場合もあります。

PORMのような軽量ツールが適切な場合もあります。
場合によっては、生のPDOで十分かもしれません。

レガシーシステムのモダナイゼーションに王道はありません。
しかし、PORMのような実践から生まれたツールは、その困難な道のりを少し楽にしてくれるかもしれません。

あなたのプロジェクトでは、どんなアプローチを取りますか?

タイトルとURLをコピーしました