ホームページ >バックエンド開発 >PHPチュートリアル >データソースアーキテクチャパターンのデータマッパー
データ ソース アーキテクチャ モードのテーブル データ エントリ、データ ソース アーキテクチャ モードの行、およびデータ エントリ データ ソース アーキテクチャ モードのアクティビティ レコードについては、これら 3 つのデータ ソース アーキテクチャ モードと比較して、データ マッパーは次のようになります。もっと「ハイエンド」に」。
1. 概念
データ マッパー: オブジェクトとデータベース (およびマッパー自体) の間でデータを相互に独立させながら移動するマッパー レイヤー。概念は常に抽象的です。簡単に言えば、データ マッパーはデータをオブジェクトにマッピングする役割を担うクラス データです。
2. データマッパーを使用する理由は何ですか?
データ マッパーは最初の 3 つのモードよりも実装が複雑ですが、なぜこれを使用するのでしょうか?
オブジェクト間の組織的な関係は、リレーショナル データベースのテーブルとは異なります。データベース テーブルは行と列で構成されるグリッドとして見ることができ、テーブル内の行は外部キーを介して別のテーブル (同じテーブルであっても) の行に関連付けることができ、オブジェクトの構成関係はより複雑になります。オブジェクトには他のオブジェクトが含まれる場合があり、異なるデータ構造が同じオブジェクトを異なる方法で編成する場合があります。
オブジェクト データベースとリレーショナル データベース間のこの相違は、「オブジェクトとリレーショナルのインピーダンス不一致」または「インピーダンス不一致」と呼ばれます。
データ マッパーは、この問題を非常にうまく解決できます。これは、オブジェクト データベースとリレーショナル データベースの間でデータを変換する役割を果たします。これにより、ドメイン モデル内のデータベース操作を効果的に隠し、データベース変換で避けられない競合を管理します。
3. データ マッパーの単純な実装
Php コード
<?php //领域抽象类 abstract class DomainObject { private $id = -1; function __construct( $id=null ) { if ( is_null( $id ) ) { $this->markNew(); } else { $this->id = $id; } } function getId( ) { return $this->id; } static function getCollection( $type ) { //这里通过一个工广生成此对象对应的数组数据对象 return HelperFactory::getCollection( $type ); } function collection() { return self::getCollection( get_class( $this ) ); } function finder() { return self::getFinder( get_class( $this ) ); } static function getFinder( $type ) { //这里通过一个工厂生成此对象对应的map对象 return HelperFactory::getFinder( $type ); } function setId( $id ) { $this->id = $id; } function __clone() { $this->id = -1; } } //场所类 class Venue extends DomainObject { private $name; private $spaces; function __construct( $id=null, $name=null ) { $this->name = $name; parent::__construct( $id ); } function setSpaces( SpaceCollection $spaces ) { $this->spaces = $spaces; } function getSpaces() { if ( ! isset( $this->spaces ) ) { //创建对应的SpaceMapper对象 $finder = self::getFinder( 'Space' ); $this->spaces = $finder->findByVenue( $this->getId() ); //$this->spaces = self::getCollection("Space"); } return $this->spaces; } function addSpace( Space $space ) { $this->getSpaces()->add( $space ); $space->setVenue( $this ); } function setName( $name_s ) { $this->name = $name_s; } function getName( ) { return $this->name; } static function findAll() { $finder = self::getFinder( __CLASS__ ); return $finder->findAll(); } static function find( $id ) { $finder = self::getFinder( __CLASS__ ); return $finder->find( $id ); } } abstract class Mapper{ protected static $PDO; function __construct() { if ( ! isset(self::$PDO) ) { //此处可加缓存 self::$PDO = new PDO( $dsn ); self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } } private function getFromMap( $id ) { //从内存取出此$id的DomainObject对象 } private function addToMap( DomainObject $obj ) { //将此DomainObject对象加入到内存 } function find( $id ) { $old = $this->getFromMap( $id ); if ( $old ) { return $old; } $this->selectstmt()->execute( array( $id ) ); $array = $this->selectstmt()->fetch( ); $this->selectstmt()->closeCursor( ); if ( ! is_array( $array ) ) { return null; } if ( ! isset( $array['id'] ) ) { return null; } $object = $this->createObject( $array ); return $object; } function findAll( ) { $this->selectAllStmt()->execute( array() ); return $this->getCollection( $this->selectAllStmt()->fetchAll( PDO::FETCH_ASSOC ) ); } function createObject( $array ) { $old = $this->getFromMap( $array['id']); if ( $old ) { return $old; } $obj = $this->doCreateObject( $array ); $this->addToMap( $obj ); return $obj; } function insert( DomainObject $obj ) { $this->doInsert( $obj ); $this->addToMap( $obj ); } protected abstract function getCollection( array $raw ); protected abstract function doCreateObject( array $array ); protected abstract function doInsert( DomainObject $object ); protected abstract function targetClass(); protected abstract function selectStmt( ); protected abstract function selectAllStmt( ); } class VenueMapper extends Mapper { function __construct() { parent::__construct(); $this->selectAllStmt = self::$PDO->prepare( "SELECT * FROM venue"); $this->selectStmt = self::$PDO->prepare( "SELECT * FROM venue WHERE id=?"); $this->updateStmt = self::$PDO->prepare( "UPDATE venue SET name=?, id=? WHERE id=?"); $this->insertStmt = self::$PDO->prepare( "INSERT into venue ( name ) values( ? )"); } function getCollection( array $raw ) { //这里简单起见用个对象数组 $ret = array(); foreach ($raw as $value) { $ret[] = $this->createObject($value); } return $ret; } protected function doCreateObject( array $array ) { $obj = new Venue( $array['id'] ); $obj->setname( $array['name'] ); //$space_mapper = new SpaceMapper(); //$space_collection = $space_mapper->findByVenue( $array['id'] ); //$obj->setSpaces( $space_collection ); return $obj; } protected function targetClass() { return "Venue"; } protected function doInsert( DomainObject $object ) { $values = array( $object->getname() ); $this->insertStmt->execute( $values ); $id = self::$PDO->lastInsertId(); $object->setId( $id ); } function update( DomainObject $object ) { $values = array( $object->getname(), $object->getid(), $object->getId() ); $this->updateStmt->execute( $values ); } function selectStmt() { return $this->selectStmt; } function selectAllStmt() { return $this->selectAllStmt; } } //client代码 $venue = new venue(); $venue->setName("XXXXXXX"); //插入一条数据 $mapper = new VenueMapper(); $mapper->insert($venue); //获取刚插入的数据 $venueInfo = $mapper->find($venue->getId()); //修改数据 $venue->setName('OOOOOOOOOOO'); $mapper->update($venue); ?>
このコードでは、いくつかの補助クラスが省略され、メインのドメイン オブジェクトとデータ マッパーが保持されます。データ マッパー パターンの最も強力な点は、ドメイン層とデータベース操作の間の結合を取り除くことです。マッパー オブジェクトはバックグラウンドで動作し、さまざまなオブジェクト リレーショナル マッピングに適用できます。これにより、多数の特定のマッパー クラスを作成する必要が生じます。しかし、現在ではプログラムを通じてフレームワークを自動生成できるようになりました。
4. 使用のタイミング
データベース マッパーを使用する主な理由は、データベース スキームとオブジェクト モデルを互いに独立して進化させる必要がある場合です。最も一般的なのは、もちろんドメイン パターンで使用されるものです。設計段階、開発段階、テスト段階のいずれであっても、データ マッパーはドメイン モデルで動作するときにデータベースを考慮する必要はありません。これらの対応はすべてデータ マッパーによって行われるため、ドメイン オブジェクトはデータベースの構造について何も知りません。
もちろん、データ マッパーは新しいレイヤーを導入するため、これらのパターンを使用するための前提条件はビジネス ロジックの複雑さですが、シンプルであればその必要はありません。
ドメイン モデルがなければデータ マッパーは使用しません。しかし、データ マッパーなしでドメイン モデルを使用できますか?ドメイン モデルが単純で、データベースがドメイン モデル開発者の制御下にある場合、ドメイン オブジェクトがアクティビティ レコードを使用してデータベースに直接アクセスすることは合理的です。
完全なデータベース マッピング レイヤーを作成する必要はありません。このようなデータ マッパーの作成は複雑です。ほとんどの場合、自分で作成するのではなく、オープンソースのデータベース マッピング レイヤーを使用することをお勧めします