Property-System

Properties sind die Eigenschaften, die einem Artikel als Metainfos, einem Benutzer als Attribut oder einem Produkt in varisale als Attribut gegeben werden können. Sie enthalten die schematische Beschreibung der Eigenschaft, wie den Titel, den internen Namen, den Datentyp und dessen Parameter sowie weitere, AddOn-spezifische Angaben.

Beginnend mit Sally 0.4 werden Properties nicht mehr in der Datenbank, sondern in YAML-Dateien definiert. Damit können sich die meisten AddOns die umfangreichen Quellcodes für das Aufbauen und Parsen der Formulare sparen. Immerhin werden Properties vom Integrator festgelegt und ändern sich im Live-Betrieb nicht mehr (im Gegensatz zu ihren Werten, die sich natürlich im Backend ändern lassen sollten).

AddOns, die Properties benutzen, bringen immer auch eine eigene API zum Zugriff auf diese mit. Die folgenden Beschreibungen sind daher für Autoren von AddOns interessant, nicht für Entwickler, die sie nutzen.

Properties definieren

Für gewöhnlich wird ein AddOn seine Properties in der globals.yml speichern. Die dort definierten Inhalte werden in den Root der Konfiguration gemerged und stehen somit über sly_Core::config()->get(...) zur Verfügung.

Der mitgelieferte, abstrakte WV_Service_Property sollte zum Laden verwendet werden. Er wird automatisch Änderungen an der Struktur erkennen und die in ihm definierten, abstrakten Hooks aufrufen. Ein AddOn sollte also einen Service von der Basis ableiten und diese Methoden definieren:

<?
class MyPropService extends WV_Service_Property {
  protected function onNew(WV_Property $property) {
    // Code, der beim Auftreten einer neuen Property ausgeführt wird.
  }

  protected function onDeleted(WV_Property $property) {
    // Code, der beim Löschen einer Property ausgeführt wird.
  }

  protected function onChangedDatatype(WV_Property $oldVersion, WV_Property $newVersion) {
    // Code, der bei Änderung des Datentyps ausgeführt wird
  }

  protected function onChangedParameters(WV_Property $oldVersion, WV_Property $newVersion) {
    // Code, der bei Änderung der Parameter ausgeführt wird
  }

  protected function onChanged(WV_Property $oldVersion, WV_Property $newVersion) {
    // Code, der bei sonstigen Änderungen ausgeführt wird
  }

  protected function createProperty($name, array $property) {
    return new MyPropertyClass($name, $property);
  }
}

In den Methoden können dann die eigentlichen Daten aktuell gehalten werden. WV_Property ist eine Basisimplementierung, die bereits die wichtigsten Eigenschaften kapselt und als Basis für eine eigene Property-Klasse (MyPropertyClass im obigen Beispiel) benutzt werden sollte.

Beispiel

Wir nehmen folgende globals.yml im AddOn “test” an:

testaddon:
   foo:
      title: Meine Property
      helptext: Hilfetext, der unter dem Eingabeelement erscheint.
      datatype: stringline
   bar:
      title: Noch eine Eigenschaft
      datatype: stringtext

Die Properties können wie folgt ausgelesen werden:

<?
$service    = new MyPropService();
$data       = sly_Core::config()->get('testaddon');
$properties = $service->loadFromArray($data);

/*
$properties = Array(
  'foo' => WV_Property{
     title:protected => 'Meine Property',
     ...
   },
  'bar' => ...
)
*/

Komplexe Hilfetexte

Seit Changeset 27b729b953fc unterstützen die Developer Utils neben den reinen Strings für Hilfetexte (siehe obiges Beispiel) auch eine erweiterte Variante. Dabei kommt ein Callback zum Einsatz, dessen Aufgabe es ist, den Hilfetext direkt an dem jeweiligen Formularelement zu setzen.

Warnung

Es handelt sich hierbei nicht um ein Feature von Sally. Die normale Formular-API von Sally (sly_Form_ElementBase->setHelpText()) hat von der ganzen Geschichte keine Kenntnis.

Integration

Die Verarbeitung der erweiterten helptext-Angabe ist von Datentypen abhängig. Nur Datentypen, die das Format explizit unterstützen, können Callbacks auswerten.

WV_Datatype steht die statische Methode setHelpTextOnElement() zur Verfügung, die die ganze Logik der Callbacks implementiert. Üblicherweise wird man diese Methode in der renderForm() des Datentyps aufrufen wollen, nachdem man das entsprechende Formularelement erzeugt hat. Im boolean-Datentyp sieht das beispielsweise wie folgt aus:

<?
class _WV_Datatype_Boolean extends _WV_Datatype_Base implements WV_IDatatype {
  public function renderForm(sly_Form $form, WV_IProperty $property, $value) {
    // (some stuff)

    $checkbox = new sly_Form_Input_Checkbox('property_'.$property->getID().'_value', $property->getTitle(), '1', $description);
    $checkbox->setMultilingual($property->isMultilingual());

    // here is where the magic happens
    WV_Datatype::setHelpTextOnElement($form, $checkbox, $property);

    $form->add($checkbox);
    return $form;
  }

  // ...
}

Konfiguration

Eine minimale Konfiguration sieht wie folgt aus:

testaddon:
   foo:
      title: 'Meine Property'
      datatype: 'stringline'
      helptext:
         callback: ['MyClass', 'myStaticMethod']  # typisches Format für Callbacks
         params: {}

Obige Konfiguration definiert, dass für den Hilfetext die Methode MyClass::myStaticMethod() ausgeführt wird. Ihm werden abgesehen von den Standard-Parametern (siehe unten) keine weiteren Parameter mitgegeben.

In vielen Fällen wird man noch weitere Daten übergeben wollen. Dies könnte wie folgt aussehen (die gesamte Struktur der params-Angabe im YAML ist vom Callback abhängig und kann frei gestaltet werden).

testaddon:
   foo:
      title: 'Meine Property'
      datatype: 'stringline'
      helptext:
         callback: ['MyClass', 'myStaticMethod']  # typisches Format für Callbacks
         params:
            myparam: 'wert'
            alist: [1,2,3]
            evenmore:
               - foo
               - bar

Alle definierten Parameter werden 1:1 dem Callback übergeben.

Der Callback

Ein Callback ist (wie der Name schon sagt) ein beliebiges PHP-Callable, also Klassenmethode oder globale Funktion. Ihm werden immer die folgenden vier Parameter übergeben:

$property
Das zu rendernde Property selbst.
$form
Das sly_Form-Objekt, indem das Formularelement gerendert werden soll.
$element
Das Formularelement, das vom Property gerendert wird. Die grundlegenden Methoden von sly_ElementBase sind immer verfügbar.
$params
Die zusätzlich definierten Parameter aus dem YAML.

Ein Callback, der obige Konfiguration auswertet, könnte wie folgt aussehen:

<?
class MyClass {
   public static function myStaticMethod(WV_Property $prop, sly_Form_ElementBase $element, sly_Form $form, array $params) {
      print_r($params);
      /*
      Array
      (
        'myparam' => 'wert',
        'alist' => Array(1,2,3),
        ...
      )
      */

      // (here some magic happens and you decide what the help text should be)

      $helptext = doMagic();

      // Der Hilfetext muss nicht zurückgegeben, sondern direkt am Element
      // gesetzt werden.

      $element->setHelpText($helptext);

      // Auf Wunsch kann auch HTML erlaubt sein (dann wird $helptext raw ausgegeben)
      // $element->setHelpTextIsHTML(true);
   }
}

Helper-Implementierung

Die Technik mit den Callbacks wurde eingeführt, um Hilfetexte dynamischer zu gestalten. Primär, um bei den Eingabemöglichkeiten, bei denen Platzhalter möglich sind, zu assistieren.

Ein gutes Beispiel dafür liefert das UserWorkflows-AddOn. Es definiert eine ganze Reihe von Global Settings, die Angaben zu eMail-Empfängern, -Inhalten usw. steuern. Innerhalb dieser Settings sind Platzhalter der Form #PLATZHALTER# erlaubt, mit denen Benutzer-Attribute in den Text eingefügt werden können (eine Anrede könnte Hallo #FIRSTNAME# #LASTNAME#! lauten).

Um nun im Backend alle verfügbaren Platzhalter anzuzeigen, müsste das AddOn bereits wissen, welche Attribute im Projekt definiert werden. Das kann es nicht. Alternativ könnte der Integrator die Hilfetexte der vielen Settings überschreiben – zeitaufwendig.

Um dem zu begegnen wurde ein kleiner Helper implementiert: WV_Form_PropertyHelper. Dieser erlaubt es, über ein paar Hilfsmethoden die Listen von Benutzerattributen (und anderen!) abzurufen und als Hilfetext darzustellen.

PHP-Seite

Um zu verstehen, wie er konfiguriert wird, sehen wir uns zuerst an, wie man ihn von Hand in eigenen Formularen (die nicht auf dem Property-Datentyp-System basieren) benutzen kann. Wir wollen dabei einem Input-Feld die Beschriftung geben, dass Benutzerattribute als Platzhalter verwendet werden können.

<?
// nichts besonderes hier
$input = new sly_Form_Input_Text('myfield', 'Mein Text', '');

// wir holen uns einen helper
$helper = new WV_Form_PropertyHelper();

// wir können den Text, der VOR der Liste der Attribute steht, selber bestimmen
$helper->setLabel('Verwenden Sie die folgenden Attribute: ');

// jetzt fügen wir dem Helper nach und nach Properties hinzu
$helper->addProperty('NAME', 'Name der Person');
$helper->addProperty('FIRSTNAME', 'Vorname der Person');
// ...

// und zuletzt lassen wir den generierten Hilfetext setzen
$helper->prepareFormElement($input);

// Hilfetext wird jetzt in etwa wie folgt aussehen:
// 'Verwenden Sie ...: <strong>#NAME#</strong> (Name der Person), <strong>#.....#</strong> (...)'

Das ist nett, aber bisher haben wir noch nichts gewonnen. Die eigentliche Hilfsfunktionalität sind die anderen add*-Methoden:

<?
// nichts besonderes hier
$input = new sly_Form_Input_Text('myfield', 'Mein Text', '');

// wir holen uns einen helper
$helper = new WV_Form_PropertyHelper();

// füge alle kategorie-metainfos hinzu
$helper->addCategoryMetainfos();

// füge alle frontenduser-attribute für benutzertyp 'default' hinzu
$helper->addFrontendUserAttributes('default');

// und noch die restlichen für den typ 'manager'
$helper->addFrontendUserAttributes('manager');

// und zuletzt lassen wir den generierten Hilfetext setzen
$helper->prepareFormElement($input);

Jetzt haben wir deutlich Code eingespart. Und gleichzeitig ist unser Formular dynamisch und passt sich den definierten Metainfos oder Benutzerattributen automatisch an.

Bemerkung

Die verfügbaren Helper sind hartcoded. Es gibt keine Möglichkeit, weitere Methoden in der Klasse zu ergänzen. Wer die Technik auch für eigene Attribute nutzen möchte, kann die ganze Klasse ableiten und erweitern oder die primitive add(name, description)-Methode verwenden.

Es stehen die folgenden Helper bereit:

  • Metainfo
    • addArticleMetainfos($articleType = null)
    • addCategoryMetainfos()
    • addMediumMetainfos()
    • addUserMetainfos()
  • FrontendUser
    • addFrontendUserAttributes($userType = null)
  • varisale
    • addVarisaleAttributes($productType = null, $fixed = true, $variable = true)

YAML-Konfiguration

Der beschriebene Helper steht in Form des Callbacks WV_Datatype::placeholders() zur Verfügung. Dieser Callback dient einzig dazu, die Nutzung von WV_Form_PropertyHelper konfigurierbar zu machen.

Dazu erwartet der Callback zwei Parameter:

groups (array)
groups ist ein Array von AddOns. Die Keys sind jeweils die Namen (metainfo, frontenduser, varisale) der AddOns und die Werte entsprechen weiterer Konfiguration. Das untenstehende Beispiel zeigt, wie es geht.
label (string, optional)
Das Label, das vor die Liste gesetzt werden soll (das ist nicht das Label des ganzen Formularelements!)

Wir wollen nun folgendes konfigurieren:

  • Verwende den Callback in WV_Datatype.
  • Setze vor die Liste den Text Platzhalter.
  • Zeige alle FrontendUser-Attribute an.
  • Zeige zusätzlich alle Benutzer-Metainfos an.
  • Zeige zusätzlich (zu Demo-Zwecken) noch alle Artikel-Metainfos des Artikeltyps standard an.
testaddon:
   foo:
      title: 'Meine Property'
      datatype: 'stringline'
      helptext:
         callback: ['WV_Datatype', 'placeholders']
         params:
            # Das Label. Unspannend.
            label: 'Platzhalter: '

            # Die Gruppen (= AddOns)
            groups:
               # FrontendUser: keine weitere Angabe (null für den Benutzertyp)
               # -> *alle* Attribute anzeigen
               frontenduser: ~

               # Metainfo
               metainfo:
                  # *alle* Benutzermetainfos
                  users: ~
                  # nur die Artikelmetainfos für den standard-Artikeltyp
                  articles: 'standard'

Bemerkung

Die varisale-Attribute können bisher noch nicht über YAML eingeschränkt werden.

And you’re done.

Inhalt

Vorheriges Thema

Developer Utils

Nächstes Thema

Datentypen

Diese Seite