トランザクションがモデルレイヤーに属していないという理由は、基本的に次のとおりです。
モデルは他のモデルのメソッドを呼び出すことができます。
モデルがトランザクションを開始しようとしたが、呼び出し元がすでにトランザクションを開始したかどうかがわからない場合、モデルは条件付きである必要があります。 @Bubbaの回答 のコード例に示すように、トランザクションを開始します 。モデルのメソッドはフラグを受け入れる必要があります。これにより、呼び出し元は、自身のトランザクションを開始することが許可されているかどうかをフラグに伝えることができます。それ以外の場合、モデルには、呼び出し元の「トランザクション中」の状態を照会する機能が必要です。
public function setPrivacy($privacy, $caller){
if (! $caller->isInTransaction() ) $this->beginTransaction();
$this->privacy = $privacy;
// ...action code..
if (! $caller->isInTransaction() ) $this->commit();
}
発信者がオブジェクトでない場合はどうなりますか? PHPでは、静的メソッドまたは単に非オブジェクト指向のコードである可能性があります。これは非常に厄介になり、モデルで多くのコードが繰り返されることになります。
これは、 Control Couplingの例でもあります。 、これは、呼び出し元が呼び出されたオブジェクトの内部動作について何かを知っている必要があるため、悪いと見なされます。たとえば、一部 モデルのメソッドの一部に$transactionalパラメーターがある場合がありますが、他のメソッドにはそのパラメーターがない場合があります。発信者は、パラメータが重要な場合をどのように知る必要がありますか?
// I need to override method's attempt to commit
$video->setPrivacy($privacy, false);
// But I have no idea if this method might attempt to commit
$video->setFormat($format);
私が提案した(またはPropelのようないくつかのフレームワークに実装された)他の解決策は、beginTransaction()
を作成することです。 およびcommit()
DBALがすでにトランザクションに含まれていることを認識している場合はno-ops。ただし、モデルがコミットしようとして、実際にはコミットされていないことがわかった場合、これは異常につながる可能性があります。または、ロールバックを試みて、その要求を無視します。これらの異常については以前に書いたことがあります。
私が提案した妥協案は、モデルはトランザクションについて知らないということです。 。モデルは、setPrivacy()
へのリクエストかどうかを知りません。 すぐにコミットする必要があるものですか、それとも全体像の一部であり、複数のモデルを含むより複雑な一連の変更であり、のみ これらすべての変更が成功した場合はコミットされます。 それがトランザクションのポイントです。
それで、モデルが自分のトランザクションを開始してコミットできるかどうか、またはすべきかどうかわからない場合、誰がそうしますか? GRASPには、コントローラーパターン が含まれています。 これはユースケースの非UIクラスであり、そのユースケースを実現するためにすべての要素を作成および制御する責任が割り当てられています。 コントローラーはトランザクションについて知っています これは、完全なユースケースが複雑であるかどうかに関するすべての情報にアクセスできる場所であり、1つのトランザクション内(または複数のトランザクション内)でモデルで複数の変更を行う必要があるためです。
私が以前に書いた例、つまりbeforeAction()
でトランザクションを開始することです MVCコントローラーのメソッドであり、afterAction()
でコミットします メソッドは、簡略化です 。コントローラは、現在のアクションを完了するために論理的に必要な数のトランザクションを自由に開始およびコミットできる必要があります。または、コントローラーが明示的なトランザクション制御を控え、モデルが各変更を自動コミットできるようにする場合もあります。
ただし、重要なのは、必要なトランザクションに関する情報は、モデルが認識していない情報であるということです。つまり、モデルに通知するか($ transactionalパラメーターの形式で)、呼び出し元からクエリを実行する必要があります。とにかく、コントローラーのアクションまで質問を委任する必要があります。
サービスレイヤー を作成することもできます。 そのような複雑なユースケースを実行する方法と、すべての変更を単一のトランザクションに含めるかどうかをそれぞれが知っているクラスの数。そうすれば、多くの繰り返しコードを回避できます。ただし、PHPアプリに個別のサービスレイヤーが含まれることは一般的ではありません。コントローラのアクションは通常、サービスレイヤーと一致します。