Alles klar machen zum Entern!

Mit dem Schreibvorgang an einem DataObject lässt sich in SilverStripe auf einfache Weise ein Algorithmus verknüpfen. Im Klartext: Jedes mal, wenn ein DataObject gespeichert, verändert oder gelöscht wird, wird ein Block von Anweisungen ausgeführt. Dieser Mechanismus wird als Hook bezeichnet. Dazu gibt es die folgenden Methoden:

onBeforeWrite()
onAfterWrite()
onAfterDelete()
onBeforeDelete()

Die Methodennamen dürften für sich selbst sprechen. Jedes mal, wenn die Methode DataObject->write() respektive DataObject->delete() aufgerufen wird, wird der Hook aufgerufen.

Aus der Praxis

In einem aktuellen Projekt arbeiten wir an einem Supportticketsystem. Jedes mal wenn ein Admin eine Antwort (Klasse Message) auf ein Ticket schreibt, soll der Nutzer eine Emailbenachrichtigung bekommen. In der Klasse Message überschreibt man dazu die geerbte Methode onAfterWrite():

Message extends DataObject {
      ...
      onAfterWrite() {
              $email = new Email($from, $to, $subject, $body);
              $email->send();
              parent::onAfterWrite();
      }
}

Am Ende des Anweisungsblocks muss parent::onAfterWrite() aufgerufen werden, sonst würde der Hook nicht funktionieren.

Hooks mit Haken

Wir verwalten unsere Message-Objekte natürlich geschickt mit einer Ableitung der Klasse ModelAdmin. Jetzt wurde die Email aus dem Hook bei Speichern einer Nachricht über das Backend gleich zweimal verschickt. Ein Blick in den Code von ModelAdmin lieferte hier schnell die Erklärung :

//Code ab Zeile 827
function doCreate($data, $form, $request) {
           $className = $this->getModelClass();
           $model = new $className();
           // We write before saveInto, since this will let us save has-many and many-many relationships :-)
           $model->write();
           $form->saveInto($model);
           $model->write();

Unser Hook wird durch den zweifachen Aufruf der Methode write() natürlich auch zweimal ausgelöst. Zum Glück gibt es hier den Zeilenkommentar, denn ich hätte das sonst vielleicht für einen Programmierfehler gehalten und auf einen write() Aufruf verkürzt und mir damit arge Probleme eingehandelt. Das zeigt, wie wichtig Kommentare sein können.

An die Riemen, Männer!

So eine Herausforderung will natürlich gemeistert werden. Jede Email doppelt zu bekommen, kann beim Kunden auf die Dauer zu Schizophrenie führen ;) Wir haben deshalb aus gesundheitlichen Gründen unserer Klasse Message ein boolsches Attribut "sent" gegeben:

Message extends DataObject {
...
      onAfterWrite() {
              if (false === $this->sent) {
                      $email = new Email($from, $to, $subject, $body);
                      $email->send();
                      $this->sent = true;
              }
              parent::onAfterWrite();
      }
}

Boolsche Attribute sind in SilverStripe per Default immer "false". Beim zweiten Aufruf des Hooks wird durch die if-Abfrage natürlich keine Email mehr geschickt.

Tags: