FormBuilder - Feld nicht in data eintragen

  • Hallo,

    kann man bei SingleSelectionFormField irgendwie einstellen, dass das Feld nach dem Absenden nicht im data-Array von $this->form->getData() ist, sondern zum Beispiel in data2? Ich möchte, dass dieses Feld nicht mit an die DB übergeben wird, sondern vorm Speichern separat verarbeiten.

  • Validation and Data - WoltLab Suite Documentation
  • Habe mir jetzt eine Klasse gebaut, die von SingleSelectionFormField erbt und das wie folgt gemacht:

    Danke dir :)

  • Hallo,

    erbt

    bitte.nicht.erben. An bestehende Klassen kann ich leider kein final dran schreiben ;(

    Benutz' (ungetestet) Composition:

    Diese ziemlich redundante Boilerplate kann man ggf. dann in ein AbstractFormFieldDecorator auslagern. Im Zweifel freue ich mich da auch über PRs.

  • Wäre hier jetzt nicht noch ein extends AbstractFormField wichtig? Ansonsten sagt mir mein Intellisense:

    Code
    does not implement methods 'addValidationError', 'addValidator', 'getFieldHtml', 'getJavaScriptDataHandlerModule', 'getObjectProperty', 'getSaveValue', 'getValidationErrors', 'getValidators', 'getValue', 'hasValidator', 'isRequired', 'updatedObject', 'objectProperty', 'readValue', 'removeValidator', 'required', 'value', 'getParent', 'parent', 'addClass', 'addClasses', 'addDependency', 'attribute', 'available', 'cleanup', 'checkDependencies', 'getAttribute', 'getAttributes', 'getClasses', 'getDependencies', 'getDocument', 'getHtml', 'getHtmlVariables', 'getId', 'getPrefixedId', 'hasAttribute', 'hasClass', 'hasDependency', 'id', 'removeAttribute', 'removeClass', 'removeDependency', 'validate', 'create', 'validateAttribute', 'validateClass', 'validateId', 'description', 'getDescription', 'getLabel', 'label', 'requiresLabel'
  • Effektiv kannst du einfach die Klasse SingleSelectionFormField 1:1 kopieren und dann deine Änderungen an der Klasse vornehmen. Dann ist Tim zufrieden und du musst nicht alles selbst implementieren bzw. kannst es von Zeit zu Zeit diffen.

    Genau das ist übrigens auch der Grund, warum es Vererbungen gibt; damit man eben nicht jeden Furz kopieren muss, nur, weil man einen Parameter ändern will/muss.

  • Hallo,

    Wäre hier jetzt nicht noch ein extends AbstractFormField wichtig? Ansonsten sagt mir mein Intellisense:

    Ich habe es nicht im Detail geprüft, vermutlich ist das AbstractFormField das falsche, da du den Großteil der Methoden an die gewrappte Klasse weitergeben möchtest. Deswegen auch der Hinweis auf ein neues AbstractFormFieldDecorator. Ich sehe auch kein Problem darin einen solchen Decorator noch mit 5.4 nachzuliefern, wenn er sauber entworfen ist.

    Effektiv kannst du einfach die Klasse SingleSelectionFormField 1:1 kopieren und dann deine Änderungen an der Klasse vornehmen.

    Das ginge auch, aber nur, wenn dann auch das Template und das dazugehörige JavaScript ebenfalls kopiert wird.

    Genau das ist übrigens auch der Grund, warum es Vererbungen gibt; damit man eben nicht jeden Furz kopieren muss, nur, weil man einen Parameter ändern will/muss.

    Nein, genau dafür ist Vererbung nicht gedacht, weil das typischerweise das Liskovsches Substitutionsprinzip verletzt. Hanashis individuelles Feld kann nicht überall da eingesetzt werden wo das ursprüngliche Feld eingesetzt werden kann. Es erweitert die Funktionalität nicht, es ändert sie. Dafür benutzt man Composition (und Interfaces). Vererbung ist wohl die am häufigsten falsch verwendete Funktionalität in der Objekt-orientierten Programmierung. Daran sind vermutlich auch die vielen schlechten Beispiele aus irgendwelchen Tutorials die sich an realen Objekten orientieren („Auto erbt von Reifenfahrzeug erbt von Fahrzeug“) schuld.

    Hier mal ein wenig Lektüre: https://www.thoughtworks.com/insights/blog/…ance-how-choose

  • Hallo,

    ich habe jetzt bisschen probiert einen Decorator zu basteln, aber ich habe das Gefühl, dass ich den völlig falschen Ansatz verfolge. Ich habe mich dabei am DatabaseObjectDecorator orientiert und folgendes gebaut:

    Problem ist jetzt, dass die Methode nicht weitergereicht werden (trotz __call), weil sie bereits im Elternelement existieren. Dann habe ich folgendes probiert:

    abstract class AbstractFormFieldDecorator implements IFormField { ... }

    und dort dann alle Funktionen vom Interface implementiert und an die Field-Klasse weitergereicht, welche im Konstruktor steht, aber bin mir hier auch noch unsicher ob das richtig wäre. Meine __call-Methode wäre auf jeden Fall falsch ^^

  • Problem ist jetzt, dass die Methode nicht weitergereicht werden (trotz __call), weil sie bereits im Elternelement existieren

    Redest du von parent::getSaveValue()? Für das Fallback müsstest du dann $this->getDecoratedField()->getSaveValue() nutzen. getDecoratedField() ist in deinem Example noch nicht implementiert - just bevor du kopierst und dich wunderst, was ich rede. ;)

    Ich hätte es so kopiert: https://github.com/mutec/WCF/comm…cb4f5b71b12080d

  • Also meine Idee wäre jetzt folgendes:

    Prinzipiell klappt das auch, aber das wäre doch ziemlich überladen, oder? ^^

  • Hallo,

    Prinzipiell klappt das auch, aber das wäre doch ziemlich überladen, oder? ^^

    Genau so soll es aussehen, du implementierst genau die Funktionen des Interfaces. Die Decorators bei den DatabaseObjects sind leider kein besonders gutes Beispiel und verdienen den Namen eigentlich nicht. Die ->getDecoratedObject()-Methode sollte eigentlich nicht existieren müssen, ist da aber dem Mangel an Interfaces geschuldet. Decorator sollen an allen Stellen des regulären Objekts als Drop-In-Replacement funktionieren.

    That said: Ein bisschen muss man noch aufräumen.

    • Entferne __call(). Die Interface-Methoden müssen für die Interoperabilität mit anderem Code ausreichend sein. Etwaige zusätzliche Methoden (mit zusätzlichem Interface) müssten dann in dem spezifischen Decorator implementiert werden (ggf. mit trait).
    • Entferne $baseClass.
    • Ergänze das fehlende abstract an der Klasse.
    • Ergänze einmal das @inheritDoc an jeder Methode.
    • Ergänze einen Klassenkommentar. Trag dich gerne als Autor ein, du hast die Arbeit gemacht.
    • Wirf in den 4 statischen Methoden jeweils eine new \BadMethodCallException('This method is available on a decorator.');. Statische Methoden sind in einem Interface absolut nutzlos (weil man eben gar kein Objekt zur Hand hat). Die FormBuilder-Interfaces sind offenbar bisweilen ein wenig überspezifisch. Ich habe eine Issue erstellt, damit man das Interface mit 5.5 ebenfalls aufräumt: https://github.com/WoltLab/WCF/issues/4468
    • Ansonsten überlege ich noch, ob man das besser als Trait oder als abstrakte Klasse löst.

    Dass das ein wenig überladen (ich würde eher sagen: „voll“) wirkt, liegt einfach daran, dass die FormField eine recht umfangreiche API haben. Aber dafür implementiert man die Methoden ja einmalig in der Elternklasse.

    Benutzung ist dafür hinterher ganz simpel:

    PHP
    <?php
    final class NoSaveValueDecorator extends AbstractFormFieldDecorator
    {
        public function hasSaveValue()
        {
            return false;
        }
    }

    oder wenn die Implementierung nur für bestimmte Klassen sinnvoll ist:

  • Hallo,

    dann wäre das jetzt die angepasste Klasse:

    Wenn das so passt, würde ich einen entsprechenden PR machen :)

  • Hallo,

    Leider hatte ich noch ein paar Spaces zu viel und musste einen korrigierenden Commit machen. Da ich zu blöd bin die Commits zu einem zusammenzufassen, sind es jetzt leider 2 geworden ^^

    da kommt ohnehin noch ein Commit für meine Annotationen. Um das Squashing würde ich ich mich hinterher kümmern, hier aber die Kurzanleitung:

    1. Die offenen Änderungen in den direkt vorhergehenden Commit integrieren oder die Commit-Nachricht anpassen: git commit --amend.

    2. Wenn ältere Commits angepasst werden müssen, dann einen Commit mit den gewünschten Änderungen erstellen und beim interaktiven Rebase git rebase -i 5.4 passend einsortieren und fixup bzw. squash statt pick wählen.

    In jeden Fall ist hinterher ein force-Push notwendig: git push origin <mybranch> --force-with-lease.

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!