2012-11-17 5 views
5

Ich bin ein wenig verwirrt. Ich benutze PHP RedBean als ORM aktiv in meinem Direct Mail-Dienst und ich stoße in eine seltsame Situation - ich habe eine Tabelle mit eindeutiger Schlüsseleinschränkung (d. H. Subscriber_id, delivery_id) und zwei Skripte, die Daten in diese Tabelle schreiben. Es gibt Quellcode, oder Aktualisierung Tabelle einfügen:PHP RedBean Shop-Bean, wenn nicht existiert ein

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); 
} 

Es genannt wird sowohl von zwei Skripten. Und ich habe regelmäßig Probleme mit der Beschränkung der Korruption auf diese Tabelle, weil Race-Bedingungen auftreten. Ich weiß über SQL "INSERT auf doppelte Schlüsselaktualisierung" Eigenschaft. Aber wie kann ich mit meinem ORM das gleiche Ergebnis erzielen?

+2

Was Sie versuchen, ist ein "Upsert". Versuchen Sie dafür zu googeln. Ich habe diese Diskussion darüber gefunden: https://github.com/gabordemooij/redbean/issues/160 –

Antwort

3

Strom, dass ich weiß, wenn Redbean wird eine

INSERT ON DUPLICATE KEY UPDATE 

als die Diskussion über diese in den Kommentaren oben zitierten nicht erteilen zeigt an, dass Upsert Redbean Entwickler hält eine Business-Logik Sache zu sein, dass die ORM verschmutzen würde Zwischenphase. Dies gesagt, es ist am ehesten erreichbar, wenn man Redbean mit einem benutzerdefinierten Query Writer oder Plugin für die Documentation erweitern würde. Ich habe das nicht versucht, weil die folgende Methode dieses Verhalten leicht erreicht, ohne mit den Interna und den Plugins des ORM zu verkehren, aber es erfordert, dass Sie Transaktionen und Modelle und ein paar zusätzliche Abfragen verwenden.

Beginnen Sie Ihre Transaktion grundsätzlich mit R :: transaction() oder R :: begin() vor dem Aufruf von R :: store(). Verwenden Sie dann in Ihrem "FUSE" d-Modell die FUSE-Methode "update", um eine Abfrage auszuführen, die nach Duplizierung sucht und die vorhandene ID abruft, während die erforderlichen Zeilen gesperrt werden (z. B. SELECT FOR UPDATE). Wenn keine ID zurückgegeben wird, sind Sie gut und lassen Sie Ihre normale Modellvalidierung (oder deren Fehlen) wie gewohnt fortfahren und zurückgeben. Wenn eine ID gefunden wird, setze einfach $ this-> bean-> id auf den zurückgegebenen Wert und Redbean wird UPDATE anstelle von INSERT. Also, mit einem Modell wie folgt aus:

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; 
    } 
} 

Sie würden dann die R ändern :: store() aufrufen, etwa so:

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

oder

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

Die Transaktion bewirkt, dass der "FOR UPDATE" -Klausel zum Sperren der gefundenen Zeile oder für den Fall, dass keine Zeile gefunden wurde, um die Stellen im Index zu sperren, an denen die neue Zeile eingefügt wird, so dass keine Nebenläufigkeitsprobleme auftreten.

Jetzt löst dies nicht ein Benutzer Update des Datensatzes Clobbering ein anderes, aber das ist ein ganz anderes Thema.

+0

Es ist nicht so elegante Lösung wie integrierte ORM-Unterstützung. –

+0

Nein, leider wurde laut der zitierten Diskussion die echte Upsert-Funktionalität als Geschäftslogik angesehen und ist nicht in den Redbean-Kern integriert. Sie können jedoch Ihren eigenen benutzerdefinierten Abfrage-Writer oder ein Plugin schreiben, das diese Art von Dingen in das Orm verschiebt. Informationen zum Schreiben dieser sind in der [Dokumentation] (http://www.redbeanphp.com/plugins/create_your_own) –

+0

Vielen Dank, aber ich fragte dies vor mehr als einem Jahr. –

Verwandte Themen