最善の行動方針は、データ アクセスにどのように取り組んでいるかによって異なります。次の 3 つの方法があります。
- ストアド プロシージャを使用する
- コード内にクエリを保持します (ただし、前述のように、すべてのクエリを関数に入れ、パラメーターに PDO を使用するようにすべてを修正します)。
- ORM ツールを使用する
独自の未加工の SQL をデータベース エンジンに渡したい場合、PHP コードから未加工の SQL を取得し、それを比較的変更せずに維持するだけであれば、ストアド プロシージャが適しています。ストアド プロシージャと生の SQL の議論は聖戦のようなものですが、K. Scott Allen は、データベースのバージョン管理に関する記事で、使い捨てではありますが、優れた点を挙げています。
第 2 に、ストアド プロシージャは私の目には好まれなくなりました。私は、ストアド プロシージャを常に使用する必要があるという WinDNA 教化派の出身です。今日、私はストアド プロシージャをデータベースの API レイヤーと見なしています。これは、データベース レベルで API レイヤーが必要な場合に適していますが、必要のない追加の API レイヤーを作成して維持するためのオーバーヘッドが発生する多くのアプリケーションを目にします。これらのアプリケーションでは、ストアド プロシージャはメリットよりも負担になります。
私は、ストアド プロシージャを使用しない傾向があります。私は、DB がストアド プロシージャを介して公開された API を持つプロジェクトに取り組んできましたが、ストアド プロシージャは独自の制限を課す可能性があり、それらのプロジェクトはすべて、程度の差こそあれ、コード内で動的に生成された生の SQL を使用して DB にアクセスします。
DB に API レイヤーを配置すると、DB チームと開発チームの間の責任が明確になりますが、クエリがコード内に保持されていた場合の柔軟性がいくらか犠牲になります。この描写から利益を得るのに十分なチーム。
概念的には、おそらくデータベースをバージョン管理する必要があります。ただし、実際には、データベースをバージョン管理するよりも、コードだけをバージョン管理する可能性の方がはるかに高くなります。コードを変更するときにクエリを変更する可能性がありますが、データベースに対して格納されたストアド プロシージャのクエリを変更している場合は、コードをチェックインするときにそれらをチェックインしていない可能性があります。アプリケーションの重要な領域に対するバージョニングの多くの利点。
ただし、ストアド プロシージャを使用しないことを選択したかどうかに関係なく、少なくとも、各データベース操作がページの各スクリプトに埋め込まれているのではなく、独立した関数に格納されていることを確認する必要があります。基本的には、DB の API レイヤーです。コードで維持およびバージョン管理されます。ストアド プロシージャを使用している場合、これは事実上、DB に 2 つの API レイヤーが存在することを意味します。1 つはコードを含み、もう 1 つは DB を含みます。プロジェクトに個別のチームがない場合、これは不必要に複雑になると感じるかもしれません。私は確かにそうします。
問題がコードの簡潔さの 1 つである場合、SQL が詰まったコードをより見やすくする方法があります。以下に示す UserManager クラスは、開始するのに適した方法です。このクラスには、「ユーザー」テーブルに関連するクエリのみが含まれています。各クエリにはクラス内に独自のメソッドがあり、クエリは準備ステートメントにインデントされ、ストアド プロシージャでフォーマットするのと同じようにフォーマットされます。
// UserManager.php:
class UserManager
{
function getUsers()
{
$pdo = new PDO(...);
$stmt = $pdo->prepare('
SELECT u.userId as id,
u.userName,
g.groupId,
g.groupName
FROM user u
INNER JOIN group g
ON u.groupId = g.groupId
ORDER BY u.userName, g.groupName
');
// iterate over result and prepare return value
}
function getUser($id) {
// db code here
}
}
// index.php:
require_once("UserManager.php");
$um = new UserManager;
$users = $um->getUsers();
foreach ($users as $user) echo $user['name'];
ただし、クエリが非常に似ていても、複雑なページング、並べ替え、フィルタリングなど、クエリ条件に膨大な数の順列がある場合は、既存のコードをオーバーホールするプロセスが必要になるものの、オブジェクト/リレーショナル マッパー ツールを使用することをお勧めします。ツールの使用は非常に複雑になる可能性があります。
ORM ツールを調べることにした場合は、 Yiiの ActiveRecord コンポーネントであるPropel、または王様の PHP ORM であるDoctrineを調べる必要があります。これらのそれぞれにより、あらゆる種類の複雑なロジックを使用して、データベースへのクエリをプログラムで構築することができます。Doctrine は最も機能が充実しており、Nested Set ツリー パターンなどをすぐに使用してデータベースをテンプレート化できます。
パフォーマンスに関しては、ストアド プロシージャが最も高速ですが、通常は生の SQL に比べてそれほど大きくはありません。ORM ツールは、非効率的または冗長なクエリ、各リクエストで ORM ライブラリをロードする際の膨大なファイル IO、各クエリでの動的 SQL 生成など、さまざまな方法でパフォーマンスに重大な影響を与える可能性があります。これらすべてが影響を与える可能性がありますが、 ORM ツールを使用すると、手動クエリを使用して独自の DB レイヤーを作成するよりもはるかに少ないコードで、利用できる機能を大幅に増やすことができます。
ただし、 Gary Richardsonの言うとおりです。コードで SQL を使用し続ける場合は、クエリを使用しているかストアド プロシージャを使用しているかに関係なく、常に PDO のプリペアド ステートメントを使用してパラメーターを処理する必要があります。入力のサニタイズは、PDO によって実行されます。
// optional
$attrs = array(PDO::ATTR_PERSISTENT => true);
// create the PDO object
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass", $attrs);
// also optional, but it makes PDO raise exceptions instead of
// PHP errors which are far more useful for debugging
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('INSERT INTO venue(venueName, regionId) VALUES(:venueName, :regionId)');
$stmt->bindValue(":venueName", "test");
$stmt->bindValue(":regionId", 1);
$stmt->execute();
$lastInsertId = $pdo->lastInsertId();
var_dump($lastInsertId);
警告: ID が 1 であると仮定すると、上記のスクリプトは を出力しますstring(1) "1"
。PDO->lastInsertId()
実際の列が整数であるかどうかに関係なく、ID を文字列として返します。PHP は文字列を整数に自動的にキャストするため、これが問題になることはおそらくないでしょう。
以下は出力されますbool(true)
:
// regular equality test
var_dump($lastInsertId == 1);
しかし、 is_intや PHP の「本当に、本当に、100% 等しい」演算子のように、値が整数であることを期待しているコードがある場合:
var_dump(is_int($lastInsertId));
var_dump($lastInsertId === 1);
いくつかの問題が発生する可能性があります。
編集:ここでストアドプロシージャに関するいくつかの良い議論