2012-11-17 4 views
5

少し混乱します。私はダイレクトメールサービスでORMとしてPHP RedBeanを積極的に使用しています。興味深い状況に遭遇しています。ユニークなキー制約(subscriber_id、delivery_id)を持つテーブルと、このテーブルにデータを書き込む2つのスクリプトがあります。 挿入されたソースコードや更新テーブルがあります:PHP RedBeanストアbeanが存在しない場合は、

public static function addOpenPrecedent($nSubscriberId, $nDeliveryId) 
{ 
    $oOpenStatBean = \R::findOrDispense('open_stat', 'delivery_id = :did AND subscriber_id = :sid', array(':did' => $nDeliveryId, ':sid' => $nSubscriberId)); 

    $oOpenStatBean = array_values($oOpenStatBean); 
    if (1 !== count($oOpenStatBean)) { 
     throw new ModelOpenStatException(
      "Ошибка при обновлении статистики открытий: пара (delivery_id, 
      subscriber_id) не является уникальной: ($nDeliveryId, $nSubscriberId)."); 
    } 

    $oOpenStatBean = $oOpenStatBean[0]; 
    if (!empty($oOpenStatBean->last_add_dt)) { 
     $oOpenStatBean->precedent++; 
    } else { 
     $oOpenStatBean->delivery_id = $nDeliveryId; 
     $oOpenStatBean->subscriber_id = $nSubscriberId; 
    } 

    $oOpenStatBean->last_add_dt = time('Y-m-d H:i:s'); 
    \R::store($oOpenStatBean); 
} 

それは両方の2つのスクリプトから呼ばれています。競合状態が発生するので、私は定期的にこのテーブルの矛盾したユニークな制約に関する問題があります。私は、SQLの "重複キー更新のINSERT"機能について知っています。しかし、私はORMを使って純粋に同じ結果を得ることができますか?

+2

あなたがしようとしているのは「アップサート」と呼ばれています。グーグルでそれを試してみてください。私はそれについてのこのディスカッションを見つけました:https://github.com/gabordemooij/redbean/issues/160 –

答えて

3

上記のコメントで引用これの議論はRedbeanの開発者はアップサートは、ORMのを汚染しますビジネス・ロジックのものであると考えていることを示してRedbeanが

INSERT ON DUPLICATE KEY UPDATE 

を発行しないだろう、場合、私は知っている、現在の間期。これは、Documentationごとにカスタムクエリーライターまたはプラグインを使用してRedbeanを拡張することが可能である可能性が高いと言われています。私はこれを試していません。これは、以下のメソッドがORMの内部とプラグインを邪魔することなくこの動作を簡単に達成するためです。ただし、トランザクションとモデル、および追加のクエリを使用する必要があります。

基本的に、R :: store()の呼び出しの前にR :: transaction()またはR :: begin()でトランザクションを開始します。次に、FUSEモデルでFUSEメソッドを使用して、FUSEメソッドを使用して重複をチェックし、既存のIDを取得して必要な行をロックする(SELECT FOR UPDATE)クエリを実行します。 idが返されない場合は、正常であり、通常のモデルの検証(またはその欠如)を通常どおりに継続して返します。 idが見つかった場合は、単純に$ this-> bean-> idを返された値に設定すると、RedbeanはINSERTではなくUPDATEになります。したがって、このようなモデルを:あなたは、その後Rを修正するだろう

class Model_OpenStat extends RedBean_SimpleModel{ 
    function update(){ 
    $sql = 'SELECT * FROM `open_stat` WHERE `delivery_id`=? AND 'subscriber_id'=? LIMIT 1 FOR UPDATE'; 
    $args = array($this->bean->deliver_id, $this->bean->subscriber_id); 
    $dupRow = R::getRow($sql, $args); 
    if(is_array($dupRow) && isset($dupRow['id'])){ 
     foreach($this->bean->getProperties() as $property => $value){ 
      #set your criteria here for which fields 
      #should be from the one in the database and which should come from this copy 
      #this version simply takes all unset values in the current and sets them 
      #from the one in the database 
      if(!isset($value) && isset($dupRow[$property])) 
      $this->bean->$property = $dupRow[$property]; 
     } 
     $this->bean->id = $dupId['id']; #set id to the duplicates id 
    } 
    return true; 
    } 
} 

::ストア()そうのように呼び出します。

\R::begin(); 
\R::store($oOpenStatBean); 
\R::commit(); 

または

\R::transaction(function() use ($oOpenStatBean){ R::store($oOpenStatBean); }); 

トランザクションが発生します見つかった行をロックするための「FOR UPDATE」節、または行が見つからなかった場合は、新しい行が移動する索引の場所をロックして、並行性の問題がないようにします。

これで、あるユーザーによるレコードの更新が解決されるわけではありませんが、別のトピックがあります。

+0

これは、統合されたORMサポートのように洗練されたソリューションではありません。 –

+0

残念ながら、引用した議論のとおり、真のアップサート機能はビジネスロジックとみなされ、Redbeanコアに統合されていません。ただし、独自のカスタム・クエリー・ライターやプラグインを作成して、このようなものをormに移すことができます。これらの記述に関する情報は、[documentation](http://www.redbeanphp.com/plugins/create_your_own) –

+0

にあります。ありがとうございますが、私はこれを1年以上前に尋ねました。 –

関連する問題