データベースクエリはページ自体から抽象化する必要がありますか?


10

PHPでページ生成を作成するとき、データベースクエリが散らかっている一連のファイルを作成していることがよくあります。たとえば、次のように、投稿に関するデータをデータベースから直接フェッチしてページに表示するクエリがあるとします。

$statement = $db->prepare('SELECT * FROM posts WHERE id=:id');
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$post = $statement->fetch(PDO::FETCH_ASSOC);
$content = $post['content']
// do something with the content

これらの迅速な1回限りのクエリは通常は小さいですが、データベースのやり取りコードの大部分が結局乱雑に見えることがあります。

場合によっては、ポスト関連のdbクエリを処理する単純な関数ライブラリを作成し、コードのブロックを単純なものに短縮することで、この問題を解決しました。

$content = post_get_content($id);

そして、それは素晴らしいことです。または、少なくとも何か他のことをする必要があるまでです。たぶん、最新の5つの投稿をリストに表示する必要があります。ええと、いつでも別の関数を追加できます。

$recent_posts = post_get_recent(5);
foreach ($recent_posts as $post) { ... }

しかしSELECT *、それは結局クエリを使用することになります。これは通常、とにかく必要はありませんが、しばしば複雑すぎて合理的に抽象化できません。最終的に私は、すべての単一のユースケースのデータベース相互作用関数の大規模なライブラリ、またはすべてのページのコード内の一連の乱雑なクエリのいずれかになります。そして、これらのライブラリを構築した後でも、以前に使用したことがない小さな結合を1つ実行する必要があることに気づき、その仕事を行うために、突然別の高度に専門化された関数を作成する必要があります。

確かに、一般的なユースケースや特定のやり取りのクエリに関数を使用することはできますが、生のクエリを書き始めるとすぐに、すべてに直接アクセスできるようになります。それとも、怠惰になって、MySQLクエリで直接実行する必要があるPHPループで処理を開始します。

インターネットアプリケーションの記述に詳しい方にお願いしたいと思います。コードの追加の行と、抽象化によって導入される可能性のある非効率性に見合う保守性の向上はありますか。それとも、直接クエリ文字列を使用するだけで、データベースの相互作用を処理するための許容できる方法ですか?


おそらく、ストアドプロシージャを使用して、乱雑なselects を「ラップ」できます
-si

回答:


7

特殊なクエリ関数が多すぎる場合は、それらを構成可能なビットに分解してみることができます。例えば

$posts = posts()->joinWithComments()->orderBy("post.post_date")->first(5);

また、覚えておくと便利な抽象化レベルの階層もあります。あなたが持っている

  1. mysql API
  2. あなたな選択としてMySQL機能、(「FOO =バーの記事から選択*」); または多分より構成可能select("posts")->where("foo = bar")->first(5)
  3. たとえば、アプリケーションドメインに固有の関数 posts()->joinWithComments()
  4. 次のような特定のページに固有の関数 commentsToBeReviewed($currentUser)

この抽象化の順序を尊重することは、保守のしやすさの点で大きな負担になります。ページスクリプトはレベル4の関数のみを使用する必要があり、レベル4の関数はレベル3の関数で記述される必要があります。これには少し時間がかかることは事実ですが、メンテナンスコストを一定に保つのに役立ちます(「ああ、まあ、彼らは別の変更が必要です!!!」とは対照的です)。


2
+1この構文は基本的に独自のORMを作成します。データベースへのアクセスが複雑になることを本当に心配していて、詳細をいじくり回すのに時間をかけたくない場合は、すでにこのことを理解している成熟したWebフレームワーク(CodeIgniterなど)を使用することをお勧めします。または、少なくともそれを使用して、xpmatteoが示したものと同様に、どのような構文糖が得られるかを確認してください。
Hartley Brody

5

懸念の分離は、読む価値のある原則です。ウィキペディアの記事を参照してください。

http://en.wikipedia.org/wiki/Separation_of_concerns

読む価値のあるもう1つの原則はカップリングです。

http://en.wikipedia.org/wiki/Coupling_(computer_science

2つの異なる懸念事項があります。1つはデータベースからのデータのマーシャリングで、もう1つはそのデータのレンダリングです。本当にシンプルなアプリケーションでは、おそらく心配する必要はほとんどありません。データベースアクセスと管理レイヤーをレンダリングレイヤーと密に結合しましたが、小さなアプリの場合、これは大した問題ではありません。問題は、Webアプリケーションが進化する傾向にあり、パフォーマンスや機能など、何らかの方法でWebアプリをスケールアップしたい場合に、いくつかの問題が発生することです。

ユーザーが作成したコメントのWebページを生成しているとしましょう。先のとがった髪のボスが来て、ネイティブアプリ(iPhone / Androidなど)のサポートを開始するように求めます。JSON出力が必要です。ここで、HTMLを生成していたレンダリングコードを抽出する必要があります。これを完了すると、2つのレンダリングエンジンを備えたデータアクセスライブラリが完成し、すべてが問題なく機能的にスケーリングされました。ビジネスロジックをレンダリングから分離することもできます。

上司がやって来て、ウェブサイトに投稿を表示したい顧客がいて、XMLが必要であり、ピークパフォーマンスが毎秒約5000件必要であると伝えました。次に、XML / JSON / HTMLを生成する必要があります。以前と同じように、レンダリングを再度分離できます。ただし、必要なパフォーマンスを快適に得るには、100台のサーバーを追加する必要があります。現在、データベースは100台のサーバーからアクセスされており、サーバーあたり数十の接続があり、それぞれが異なる要件や異なるクエリなどの3つの異なるアプリに直接公開されています。各フロントエンドマシンでデータベースにアクセスすることは、セキュリティ上のリスクであり、成長しています1つですが、そこには行きません。次に、パフォーマンスをスケーリングする必要があります。各アプリには異なるキャッシュ要件、つまり異なる懸念事項があります。これを1つの密結合層、つまりデータベースアクセス/ビジネスロジック/レンダリング層で試して管理できます。各レイヤーの懸念が相互に干渉し始めています。つまり、データベースからのデータのキャッシュ要件がレンダリングレイヤーとは非常に異なる可能性があります。ビジネスレイヤーにあるロジックは、 SQLは後方に移動します。そうしないと、レンダリングレイヤーに流れ込んでしまう可能性があります。これは、1つのレイヤーにすべてを含めることで私が見た最大の問題の1つです。鉄筋コンクリートをアプリケーションに流し込むようなもので、良い方法ではありません。

これらのタイプの問題、つまりWebサービスのHTTPキャッシング(squid / ytsなど)に対処する標準的な方法があります。memcached / redisなどのWebサービス自体内のアプリケーションレベルのキャッシュ。また、データベースのスケールアウトを開始すると、複数の読み取りホストと1つのマスター、またはホスト間で分割されたデータに問題が発生します。ユーザー「usera」がすべての書き込みリクエストで「[table / database] foo」にハッシュする場合、書き込みまたは読み取りリクエストに基づいて、またはシャーディングされたデータベースで異なる、データベースへのさまざまな接続を管理する100のホストが必要ではありません。

懸念の分離はあなたの友人であり、いつ、どこでそれを行うかを選択することは、アーキテクチャ上の決定であり、ちょっとした芸術です。非常に異なる要件を持つように進化するものの密結合は避けてください。物事を分離しておく理由は他にもたくさんあります。つまり、テスト、変更の展開、セキュリティ、再利用、柔軟性などが簡素化されます。


私はあなたがどこから来たのか理解していますし、あなたが言ったことに何も異議はありませんが、これは今のところ小さな懸念事項です。問題のプロジェクトは個人的なものであり、現在のモデルでの私の問題のほとんどは、密結合を回避するプログラマの本能にありますが、私は本当に複雑なサーバー側開発の初心者なので、これの終わりは少し続きました頭を超えて。それでも、私がこのプロジェクトについて完全にフォローしなくても、良いアドバイスだと思われるものに+1します。
Alexis King

あなたがやっていることを小さくするつもりなら、私はそれをできるだけ単純に保つでしょう。ここで従うべき良い原則はYAGNIです。
ハリー

1

「ページ自体」とは、HTMLを動的に生成するPHPソースファイルを意味すると思います。

データベースをクエリして、同じソースファイルにHTMLを生成しないでください。

PHPのソースファイルであっても、データベースにクエリを実行するソースファイルは「ページ」ではありません。

HTMLを動的に作成するPHPソースファイルでは、データベースにアクセスするPHPソースファイルで定義されている関数を呼び出すだけです。


0

ほとんどの中規模プロジェクトで使用するパターンは次のとおりです。

  • すべてのSQLクエリは、サーバー側のコードとは別の場所に配置されます。

    C#での作業は、部分クラスを使用することを意味します。つまり、単一のクラスからクエリにアクセスできる場合、クエリを別のファイルに配置します(以下のコードを参照)。

  • これらのSQLクエリは定数です。これは、SQLクエリをオンザフライで構築する誘惑を防ぐので重要です(SQLインジェクションのリスクが高まると同時に、後でクエリを確認することがより困難になります)。

C#での現在のアプローチ

例:

// Demo.cs
public partial class Demo : DataRepository
{
    public IEnumerable<Stuff> LoadStuff(int categoryId)
    {
        return this
            .Query(Queries.LoadStuff)
            .With(new { CategoryId = categoryId })
            .ReadRows<Stuff>();
    }

    // Other methods go here.
}

public partial class Demo
{
    private static class Queries
    {
        public const string LoadStuff = @"
select top 100 [StuffId], [SomeText]
    from [Schema].[Table]
    where [CategoryId] = @CategoryId
    order by [CreationUtcTime]";

        // Other queries go here.
    }
}

このアプローチには、クエリが別のファイルにあるという利点があります。これにより、DBAは、開発者によるコミットと競合することなく、クエリを確認および変更/最適化し、変更されたファイルをソース管理にコミットできます。

関連する利点は、DBAのアクセスをクエリを含むファイルのみに制限し、残りのコードへのアクセスを拒否するようにソース管理を構成できることです。

PHPで行うことは可能ですか?

PHPには部分クラスと内部クラスの両方がないため、そのままではPHPに実装できません。

DemoQueriesクラスがどこからでもアクセスできる場合、定数を含む個別の静的クラス()を使用して個別のファイルを作成できます。さらに、グローバルスコープの汚染を回避するために、すべてのクエリクラスを専用の名前空間に配置できます。これはかなり冗長な構文を作成しますが、冗長性を回避できないと思います。

namespace Data {
    public class Demo inherit DataRepository {
        public function LoadStuff($categoryId) {
            $query = \Queries\Demo::$LoadStuff;
            // Do the stuff with the query.
        }

        // Other methods go here.
    }
}

namespace Queries {
    public static class Demo {
        public const $LoadStuff = '...';

        // Other queries go here.
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.