[php]コレクションおよび永続化ファクトリー
Mapper クラスの FindById($id) は、指定された ID を持つデータをデータベースから取得し、オブジェクトにマッピングして返すことができます。多くの場合、データ コレクション (findAll) を返す必要があるため、このデータを保存し、必要に応じてオブジェクトにマップするためのデータ構造が必要になります。データの一部はオブジェクトにマップされるため、データ コレクションにはオブジェクト コレクションが必要です。データ コレクションとオブジェクト コレクションをクラスに配置すると、データからオブジェクトへのマッピングを簡単に処理できるようになります。このクラスを Collection という名前にしました。コレクション オブジェクトにアクセスしやすくするために、Collection サブクラスは、foreach を使用して簡単にアクセスできる Iterator インターフェイスを実装します。
コレクション クラス構造:
デモマッパーコレクション:
デモドメイン:
namespace demo\mapper; use demo\base\AppException; use demo\domain\DomainObject; use demo\mapper\Mapper; abstract class Collection { // 保存数据库取出的行数据 protected $raws; // 保存已映射的对象 protected $objects = array(); // 用于$raws[]到$objects的映射 protected $mapper; // 当前指针 private $pointer = 0; // 对象数组总数 private $total = 0; /** * @param array $raws 未处理过的数据库数据 * @param Mapper $mapper 用于把$raws映射成对象(createObject) */ public function __construct(array $raws = null, Mapper $mapper = null) { if (!is_null($raws) && !is_null($mapper)) { $this->raws = $raws; $this->total = count($raws); } $this->mapper = $mapper; } /** * 返回指定$num的数据对象 * @param int $num */ public function getRow($num) { if ($num < 0 || $num >= $this->total) { return null; } // 延迟加载 $this->notifyAccess(); if (isset($this->objects[$num])) { return $this->objects[$num]; } if (isset($this->raws[$num])) { $obj = $this->mapper->createObject($this->raws[$num]); $this->objects[$num] = $obj; return $obj; } return null; } /** * 添加对象 * @param DomainObject $obj * @throws AppException */ public function add(DomainObject $obj) { // 类型安全检查 $targetClass = $this->getTargetClass(); if (!($obj instanceof $targetClass)) { throw new AppException("Object must be {$targetClass}"); } // $this->notifyAccess(); $this->objects[$this->pointer++] = $obj; } public function current() { return $this->getRow($this->pointer); } public function next() { $obj = $this->getRow($this->pointer); if (!is_null($obj)) { $this->pointer++; } return $obj; } public function key() { return $this->pointer; } public function rewind() { $this->pointer = 0; } public function valid() { return !is_null($this->current()); } /** * 延迟加载 */ protected function notifyAccess() { // 暂时留空 } protected abstract function getTargetClass(); }
デモマッパー:
namespace demo\domain; use \demo\domain\DomainObject; interface ClassroomCollection extends \Iterator { public function add(DomainObject $obj); } interface StudentCollection extends \Iterator { public function add(DomainObject $obj); } interface ScoreCollection extends \Iterator { public function add(DomainObject $obj); }
namespace demo\mapper; class ClassroomCollection extends Collection implements \demo\domain\ClassroomCollection { protected function getTargetClass() { return '\demo\domain\Classroom'; } } class StudentCollection extends Collection implements \demo\domain\StudentCollection { protected function getTargetClass() { return '\demo\domain\Student'; } } class ScoreCollection extends Collection implements \demo\domain\ScoreCollection { protected function getTargetClass() { return '\demo\domain\Score'; } }
ここで、構造が少し複雑になり始めています。Mapper と Collection の特定のサブクラスを管理するために、抽象ファクトリーを使用してオブジェクトの作成を管理できます。クラス図を見てみましょう:
デモマッパーPersistanceFatory
このようなファクトリ パターンを使用すると、指定した Mapper および Collection のサブクラスを簡単に作成できると同時に、将来の新しい機能の追加も容易になります。
コレクション オブジェクトはドメイン パッケージにも必要ですが、マッパーでコレクションから分離する必要があります。ドメイン パッケージ内に HelperFactory クラスを作成し、ドメインからマッパーにアクセスするためのブリッジとして機能させることができます。
namespace demo\mapper; /** * 持久化工厂 */ abstract class PersistanceFactory { public static function getFactory($targetClass) { switch ($targetClass) { case '\demo\domain\Classroom': return new ClassroomPersistanceFactory(); case '\demo\domain\Student': return new StudentPersistanceFactory(); case '\demo\domain\Score': return new ScorePersistanceFactory(); } } public abstract function getMapper(); public abstract function getCollection(array $raws = null); } class ClassroomPersistanceFactory extends PersistanceFactory { public function getMapper() { return new ClassroomMapper(); } public function getCollection(array $raws = null) { return new ClassroomCollection($raws, $this->getMapper()); } } class StudentPersistanceFactory extends PersistanceFactory { public function getMapper() { return new StudentMapper(); } public function getCollection(array $raws = null) { return new StudentCollection($raws, $this->getMapper()); } } class ScorePersistanceFactory extends PersistanceFactory { public function getMapper() { return new ScoreMapper(); } public function getCollection(array $raws = null) { return new ScoreCollection($raws, $this->getMapper()); } }
これにより、ドメイン パッケージとマッパー パッケージが分離されます。
namespace demo\domain; use demo\mapper\PersistanceFactory; class HelperFactory { public static function getCollection($targetClass) { $fact = PersistanceFactory::getFactory($targetClass); return $fact->getCollection(); } public static function getFinder($targetClass) { $fact = PersistanceFactory::getFactory($targetClass); return $fact->getMapper(); } }
例:
namespace demo\mapper; use demo\base\AppException; use \demo\base\ApplicationRegistry; /** * Mapper */ abstract class Mapper { //... /** * 返回Collection */ public function findAll() { $pStmt = $this->getSelectAllStmt(); $pStmt->execute(array()); $raws = $pStmt->fetchAll(\PDO::FETCH_ASSOC); $collection = $this->getCollection($raws); return $collection; } /** * 返回子类Collection * @param array $raw */ public function getCollection(array $raws) { return $this->getFactory()->getCollection($raws); } /** * 返回子类持久化工厂对象 */ public function getFactory() { return PersistanceFactory::getFactory($this->getTargetClass()); } //.... }
HelperFactory はマッパー パッケージとドメイン パッケージを分離します。
$fact = PersistanceFactory::getFactory('\demo\domain\Classroom'); $mapper = $fact->getMapper(); $classrooms = $mapper->findAll(); foreach ($classrooms as $elem) { var_dump($elem); }