Data Mapper パターンに固執したい場合は、すべての依存関係 (メーカー、在庫、税金) を含むデータベースから製品を読み込むか、遅延読み込みを使用できます。最初のオプションはお勧めできません。ただし、遅延読み込みを使用するには、仮想プロキシを介して取得した追加のレイヤーが必要になります。
なんで?そうしないと、エンティティ内にいくつかのデータベース コードを配置する必要があり、それらのレイヤーに関する全体的なアイデアが分離されているためです。
では、仮想プロキシとは何ですか?
Martin Fowler (2003) によると:
仮想プロキシは、フィールドにあるはずのオブジェクトのように見えるオブジェクトですが、実際には何も含まれていません。そのメソッドの 1 つが呼び出されたときにのみ、データベースから正しいオブジェクトがロードされます。
例
インターフェイスは、実際のエンティティと仮想プロキシによって実装されるメソッドを定義します。
// The interface
interface ManufacturerInterface
{
public function getName();
}
これはエンティティです。おそらくいくつかのジェネリック モデル クラスも拡張しています。
// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
}
そして、これはプロキシです:
// The proxy
class ManufacturerProxy implements ManufacturerInterface
{
private $dao;
private $manufacturer;
private $manufacturerId;
public function __construct($dao, $manufacturerId)
{
$this->dao = $dao;
// set manufacturer to NULL to indicate we haven't loaded yet
$this->manufacturer = null;
}
public function getName()
{
if ($this->manufacturer === null)
{
$this->lazyLoad();
}
return $this->manufacturer->getName();
}
private function lazyLoad()
{
$this->manufacturer = $this->dao->getById($this->manufacturerId);
}
}
プロキシは、製品など、製造元と何らかの関係を持つ他のクラスをインスタンス化するときに、Data Mapper によって使用されます。そのため、製品がインスタンス化されると、そのフィールドが入力され、manufacturer フィールドは Manufacturer の代わりに ManufacturerProxy のインスタンスを受け取ります。
Data Mapper の実装は異なるため、簡単な例を示します。
class ProductMapper {
private $dao;
private $manufacturerDao;
// .......
public function find($id) {
// call the DAO to fetch the product from database (or other source)
// returns an associative array
$product = $this->dao->find($id);
// instantiate the class with the associative array
$obj = new Product($product);
// create the virtual proxy
$obj->manufacturer = new ManufacturerProxy($this->manufacturerDao, $product['manufacturer_id']);
return $obj;
}
}
前述したように、上記の例は非常に単純です。あなたが使用している DAO を含めましたが、Martin Fowler のような作成者は、SQL クエリやその他の基礎となるテクノロジを処理するタスクを Data Mapper に任せています。しかし、Nock (2004) のように、Data Mapper と Data Accessor の両方を採用している著者もいます。
より複雑な Data Mapper では、XML や YAML のような言語、Doctrine や Hibernate に似た言語のファイルしか持つことができませんでした。
最後に、サービス:
class ProductService {
private $productMapper;
/**
* Execute some service
* @param {int} $productId The id of the product
*/
public function someService($producId) {
// get the product from the database
// in this case the mapper is handling the DAO
// maybe the data mapper shouldn't be aware of the DAO
$product = $this->productMapper->find($productId);
// now the manufacturer has been loaded from the database
$manufacturerName = $product->manufacturer->getName();
}
}
おそらく、DAO で Data Mapper を呼び出してエンティティを作成し、サービスで DAO を使用することができます。メソッドを 2 回繰り返す必要がないため、実際にはより簡単ですが、それはあなた次第です。
オルタナティブ
仮想プロキシを実装したくないが流暢なインターフェイスが必要な場合は、Active Record パターンに切り替えることができます。PHP-ActiveRecordやParisなど、この作業を実行できるライブラリがいくつかあります。または、自分でやりたい場合は、こちらとこちらをご覧ください。Martin Fowler の本も良いスタートです。
参考文献
フォウラー、マーティン。エンタープライズ アプリケーション アーキテクチャのパターン。Addison-Wesley、2003 年。NOCK、CLIFTON。データ アクセス パターン: オブジェクト指向アプリケーションにおけるデータベースの相互作用。アディソン・ウェズリー、2004年。