現在のプロジェクトでは、コードをリファクタリングしてDBALを取得しています。データベーステーブルをモデル化するすべてのクラスの基本クラスである_class Entity
_があります。したがって、Entity
、Document
など、Article
から継承するクラスがいくつかあります。
_abstract class Entity {
/** @var DatabaseRequest $dbReuest */
protected $dbRequest;
/** @var Login $login */
protected $login;
/* some methods like insert(), update(), JsonSerialize(), etc.
}
_
これらすべてのクラスには同じコンストラクタ__construct( DatabaseRequest $dbRequest, Login $login )
があり、これらの2つのパラメータを使いたくないので、これも作成しました。
_class EntityFactory {
public function __construct( DatabaseRequest $dbRequest, Login $login )
{
$this->dbRequest = $dbRequest;
$this->login = $login;
}
public function makeEntity( string $class )
{
if ( $this->extendsEntity( $class ) ) {
$reflection = new \ReflectionClass( $class );
$construct = $reflection->getConstructor();
$params = $construct->getParameters();
return new $class( clone $this->dbRequest, clone $this->login );
}
throw new APIException( "Class $class does not extend " . Entity::class, JsonResponse::DEBUG );
}
}
_
次のようなメソッドを呼び出します:$factory->makeEntity( Document::class )
これにより、そのクラスのオブジェクトが取得されます。
このように、Entity
コンストラクターの変更により、リファクタリングの労力が最小限に抑えられました。ただし、Entity
Iを拡張するクラスでは、テーブル間の関係についていくつかのメソッドも定義しました。例えば。:
_class DocumentAddressee extends Entity {
/* ... */
public static function createFromCustomer( Address $address )
{
$self = new DocumentAddressee( clone $address->dbRequest, clone $address->login );
/* transferring data from Address to $self */
return $self;
}
}
_
( verraes.net によれば、これは、名前付きコンストラクタとしての静的メソッドの合法的な使用です)。
そして、このようなメソッドはかなり頻繁に発生します(テーブル内の外部キーごとにおよそ1〜2つのメソッド)。この方法で依存データに簡単にアクセスできるので、これらのメソッドを保持したいと思います。ただし、これらのコンストラクターをファクトリーに保持したいので、Entityコンストラクターが変更されたときに100以上のEntityクラスすべてをリファクタリングする必要はありません(これは、将来QueryBuilderを使用する場合に発生する可能性があります。
これらのメソッドを処理するためのベストプラクティスはすでにありますか?これらの関係をファクトリ内で適切に処理するか、それらの関係を追加のクラスでモデル化する必要がありますか?
理想的には、一貫性のためにこれらのオブジェクトを作成するためにファクトリを使用するだけです。したがって、工場では次のようなものが得られます。
_class EntityFactory {
public function __construct( DatabaseRequest $dbRequest, Login $login )
{
$this->dbRequest = $dbRequest;
$this->login = $login;
}
public function create($className)
{
$reflection = new \ReflectionClass($className);
if ($reflection->implementsInterface(Initialisable::class)) {
throw new APIException( "Please use createFromEntity() to create $className");
}
return new $class( clone $this->dbRequest, clone $this->login );
};
public function createFromEntity(Entity $entity, $className)
{
$newEntity = $this->create($className);
return $newEntity->initWithEntity($entity);
}
}
_
基本的な実装として、initWithEntity()
をEntity
クラスに追加することができます。
_class Entity {
public function initWithEntity(Entity $entity) {
$this->dbRequest = clone $entity->dbRequest;
$this->login = clone $entity->login;
return $this;
}
}
_
そして、更新されたInitialisable
を備えたDocumentAdressee
インターフェイスは次のとおりです。
_interface Initialisable {
public function initWithEntity(Entity $entity);
}
class DocumentAdressee implements Initialisable {
// got initWithEntity() from parent class
}
_
initWithEntity()
を使用すると、Entity
のEntityFactory
プロパティへのアクセスを開く必要がないため、リフレクションを使用する必要がありません。 DocumentAdressee
オブジェクトを作成するには、以下を使用します。
_$factory->createFromEntity($address, DocumentAdressee::class);
_
これは、エンティティが1対1の関係を持つことができる、たとえば、 DocumentAdressee
。 1対多の関係があるクラスの場合、initWithEntities()
のようなものを作成し、Entity
sの配列を渡す必要があります。
ベストプラクティスについては、使用または確認できるORMがあります。教義。
編集
作成するクラスをエンティティで初期化する必要があるかどうかを確認できるように、Initialisable
インターフェイスを追加しました。