21

データマッパーはほぼ完成しましたが、今は人間関係の段階にあります。

ここで私の考えを説明しようと思います。このトピックに関する良い記事や情報を見つけることができなかったので、おそらく私は車輪の再発明をしているのかもしれません(確かに、私は大きなフレームワークを使用できますが、それを実行して学びたいと思います)。

1:1の関係

まず、1:1の関係を見てみましょう。一般に、「Company」というドメインクラスと「Address」というドメインクラスがある場合、Companyクラスにはaddress_idのようなものがあります。ほとんどの場合、会社のリストを表示するだけで、住所は誰かが詳細を確認するときにのみ必要であるとしましょう。その場合、私のデータマッパー(CompanyDataMapper)は単に遅延ロードします。つまり、データベースからそのaddress_idをフェッチするだけで、アドレスデータを取得するための結合は行いません。

一般的に、私はすべての関係に対してゲッターメソッドを持っています。したがって、この場合、getAddress(Company companyObject)メソッドがあります。会社のオブジェクトを取得し、そのアドレスプロパティを検索し、NULLの場合は、そのAddressオブジェクトのMapperクラス(AddressDataMapper)を使用して、データベースから対応するAddressオブジェクトをフェッチし、そのアドレスオブジェクトを指定されたアドレスプロパティに割り当てます。会社オブジェクト。

重要:データマッパーは別のデータマッパーを使用できますか?

ほとんどの場合、会社オブジェクトと住所オブジェクトの両方が必要であるとしましょう。これは、常にリストにまとめて表示するためです。この場合、CompanyDataMapperは会社オブジェクトをフェッチするだけでなく、JOINを使用してSQLクエリを実行し、アドレスオブジェクトのすべてのフィールドも取得します。最後に、レコードセットを繰り返し処理し、新しいオブジェクトに対応する値をフィードして、アドレスオブジェクトを会社オブジェクトに割り当てます。

これまでのところ、単純に聞こえます。

1:n関係

これらはどうですか?1:1との唯一の違いは、会社が複数のAddressオブジェクトを持っている可能性があることです。見てみましょう:ほとんどの場合、会社にのみ関心がある場合、データマッパーは会社オブジェクトのaddressesプロパティをNULLに設定するだけです。アドレスプロパティは、なし、1つ、または複数のアドレスを参照できる配列です。ただし、読み込みが遅いため、まだわかりません。NULLです。しかし、ほとんどの場合、すべてのアドレスが必要になるとしたらどうでしょうか。すべての会社とそのすべての住所を含む大きなリストを表示するとしたらどうでしょうか。この場合、物事は本当に醜くなり始めます。まず、すべてのアドレスオブジェクトに対してアドレステーブルを50回結合することはできません(それは不可能であると強く信じています。そうすると、パフォーマンスはゼロ未満になります)。ですから、これをさらに先に考えると、

重要:これは本当ですか?10個のアドレスごとに10個の会社がある場合、100個のアドレスオブジェクトを取得するには100個のクエリを送信する必要がありますか?

m:n関係

アドレスオブジェクトに国、州、市、道路、家番号のみが含まれているとします。しかし、1つの家は、多くの企業が入っている大きなビジネスタワーになる可能性があります。誰もがそのウェブサイトでその塔を誇示するために小さなROMを借りることができるそれらの近代的なオフィスビルの1つのように。つまり、多くの企業が同じアドレスを共有できます。

そのような問題に対処する予定はまだありません。

重要:おそらくそれは1:nの関係よりも大きな問題ではありませんか?

これを解決/実装するための詳細を説明する優れたリソースを誰かが知っているなら、私はリンクについて喜んでいます!

4

3 に答える 3

17

始める前に、Fowler の PoEAA の本を最初から最後まで読んだことがあると思います。=) また、ORM を扱う際に直面する最初の問題については既に考えていると思います。同じ識別子を使用して DataMapper を複数回呼び出し、常に同じオブジェクトを返す (IdentityMap として読み取る) など、簡単なものを強調できます。

重要: Data Mapper は別の Data Mapper を使用できますか?

2 番目が 2 番目の弱い参照である場合にのみ、1 つの DataMapper が別の DataMapper にアクセスすることが可能です。

ほとんどの場合、会社オブジェクトと住所オブジェクトの両方が必要であるとしましょう。これは、常にリストにまとめて表示するためです。この場合、CompanyDataMapper は会社オブジェクトを取得するだけでなく、JOIN を使用して SQL クエリを実行し、住所オブジェクトのすべてのフィールドも取得します。最後に、レコード セットを反復処理し、新しいオブジェクトに対応する値を入力して、住所オブジェクトを会社オブジェクトに割り当てます。

ここで議論しようとしている問題は、実際には単純に聞こえますが、舞台裏では少し複雑です。

まず第一に、getAddress(Company) を持つべきではなく、代わりに Proxy オブジェクトを持つことで利益を得るべきです。プロキシは、特定のインスタンスの初期化されていない表現です。この場合、プロキシには、探しているエントリへの参照が含まれています。元のオブジェクトから拡張する必要があり、それをロードする関連する DataMapper と共に初期化メソッドを提供する必要があります。

一度に複数のオブジェクトを結合してロードすることに関する 2 番目の部分は、Hydrator と呼ばれます。ハイドレーターは、行と列のフラットな構造を受け取り、オブジェクト グラフに変換します。しかし、それは実際には別の問題に入ります: 純粋にオブジェクトを扱っているのなら、なぜテーブルをフェッチするのでしょうか? オブジェクト フェッチ アプローチを採用しようとすると、一種の OQL (Object Query Language) を実装することになります。

重要: これは本当ですか? 10 のアドレスごとに 10 の会社がある場合、100 のアドレス オブジェクトを取得するために 100 のクエリを送信する必要がありますか?

オブジェクトのコレクションを扱うことは、PHP では悪夢です。はい、強力なコレクションの実装がないため、この言語は非常に役に立ちません。基本的に、ここではさまざまな状況に対処する必要があります。 - 新しいインスタンスと、この要素リスト内のすべての要素が新しい - 新しいインスタンスと、この要素リスト内のすべての要素が既存のもの - この要素リスト内の新しいインスタンスと要素新しいものと既存のものとが混在している - 既存のインスタンスであり、要素のリストに何も触れていない - 既存のインスタンスであり、リストのアイテムを操作している

ここでは非常に単純化していますが、強調したい主なポイントは Collection オブジェクトの必要性です。それらには 2 つあります。1 つは新しいリストを処理するもので、もう 1 つは既存のリストを処理するものです。既存のリストを扱うものは、コレクション内の何かにアクセスしようとすると、コレクションをロードできる必要があります。それが n + 1 の問題を起こさない唯一の方法です。

ここでは、対処しなければならない次の大きな問題も強調しています。アソシエーションは、単方向または双方向にすることができます。これは、Company は Address を認識していますが、Address は Company を認識していないことを意味します。一方、User は多くのグループの一部であり、Groups には多くの Users が含まれていますが、双方向の関連付けです。ここでは物事が悪夢になりやすいため、何が起こっているのかを正しく理解するためにパターンのマッピングが必要です。

多対多の処理は、一般的なコレクションの処理とまったく同じです。

まだ考慮していない重要な部分があります。オブジェクト グラフ全体 (Company と Address) を作成し、それらを永続化することにした場合、両方を永続化する必要がありますか、または永続化するものを手動で指定する必要がありますか? どちらの方法にも、さまざまな問題があります。最初のアプローチが必要だとしましょう。実装する最も複雑な設計パターンの 1 つと私が考えるもの、UnitOfWork を入力しました。次に、制約の問題を発生させないように、適用されるエンティティの順序を並べ替える必要があります (これを解決する方法については、トポロジカル 並べ替えを参照してください)。2 番目のアプローチを取ると、ツールが壊れていると感じる状況に簡単に陥る可能性があります。これは主に、オブジェクト グラフが一貫性のない状態になりやすいためです。

最後に...継承のサポートを行う予定はありますか? 肯定的な場合、計画全体がまったく新しいレベルに入ったということです。=( 説明しようとすると本が必要になります。しかし、具体的なテーブルの継承 (1 クラス、1 テーブル)、単一テーブルの継承 (N クラス、1 テーブル)、およびクラス テーブルの継承 (Nクラス、M テーブル)。

ここではさまざまな点について詳しく説明できますが、通常、ORM は頭の爆発につながります。とりあえずやめます。

PS: 私は Doctrine ORM のコア開発者の 1 人です。研究目的でこれを行っている場合を除き、わざわざ別のものを作成しようとしないでください。これは非常に複雑で時間がかかり、最初の行をコーディングする前に、物事がどのように機能するかについて多くの計画を立てる必要があります。実際のところ、Doctrine ORM を 2 年間計画し、コア機能を確実に実装するのに 1 年かかりました。がっかりさせているわけではありませんが、Fowler が ORM のヘイト記事で述べたように、ORM は複雑な問題に対する複雑な解決策です。

于 2014-01-15T14:29:31.943 に答える
3

このトピックに関する回答をお待ちしておりますが、それまでの間、Amazon (または地元の書店) に飛び乗って、最終的に購入しないでください。

これらの本には、さまざまな質問で指摘されたオリジナルのパターンが含まれており、デザイン パターンとソフトウェア アーキテクチャの参考資料と見なされます。

于 2009-12-28T22:31:26.953 に答える
0

私もこの問題に取り組んでいます。まず、Matt Zandstra のPHP Objects, Patterns, and Practice (2d Ed) から Data Mapper パターンを採用しました。新刊が出たのを今知りました

おそらくセットアップの最も巧妙な部分は、「コレクション」オブジェクトです。あなたが何語を使っているか分からないので、詳細は割愛します。PHP には、最初に配列 (他の言語ではマップ) をロードし、ループ中にその場で生データをオブジェクト (ハイドレート?) に変換できる Iterator インターフェースがあると言えば十分です。

あなたと同じように、私は関係をロードする方法に苦労しています。これまでに発見したことは、大規模な JOIN クエリを Mapper クラスに記述し、ターゲット オブジェクトの脱水コレクションを作成すると同時に、関連するオブジェクトのデータをこっそり取り込むことができるということです。

非常に多くのデータベースクエリにつながるため、「遅延ロード」は本当に嫌いです。数十または数百のクエリを使用して 1 つのクエリで実行できることを知ると、私の完璧主義者の感性が不快になります。

私もまた、より多くの答えを楽しみにしています。

于 2010-07-02T13:23:22.120 に答える