Formulare verarbeiten und überprüfen mit meinem Framework

Es ist an der Zeit für einen neuen Artikel über mein MVC-Framework. Diesmal dreht sich alles rund um die dynamische Erstellung und Validierung (Überprüfung) von Formularen. Es soll möglich sein, ohne HTML-Kenntnisse, Formulare erstellen zu können. Doch das ist nicht der Hauptgrund für mein Vorhaben. Der wichtigste Anlass für diese Idee war die Tatsache, dass es bei sehr vielen Projekten immer wieder gleiche Formulare gibt und diese immer wieder auf die selbe monotone Art validiert werden müssen. Und da mein Framework ja eine Erleichterung für den Entwickler sein soll, soll es auch diese Aufgabe so weit wie möglich übernehmen. Ich habe auch schon mit der Implementierung dieser Idee angefangen und zeige euch hier den groben Aufbau und den Zusammenhang aller Klassen. Später wird relevanter Code erklärt. Wie hängt was miteinander zusammen? Es gibt eine Klasse namens FW_Form, die das eigentliche Formular repräsentiert. Eine Instanz von FW_Form kann Instanzen der Klasse FW_Element_Abstract aufnehmen. Das kann z.B. ein Eingabefeld sein. Ein FW_Form_Element_Abstract-Objekt kann widerum – ähnlich wie beim Zend-Framework – Instanzen der Klasse FW_Form_Decorator_Abstract aufnehmen. Decorators sind Klassen, die z.B. ein Label darstellen. So kann jedem Eingabefeld auf einfache Weise eine Beschriftung angefügt werden. Außer FW_Form_Decorator_Abstract gibt es noch FW_Validator_Abstract. Auch Validatoren können einem Element hinzugefügt werden. Validatoren überprüfen die Eingabe im zugehörigen Feld und geben bei bestandener Validierung true, ansonsten false zurück. Die Assoziationen sind also unidirektional und folgen dieser Hierarchie:

  • FW_Form
    • FW_Form_Element_Abstract
      • FW_Form_Element_Decorator
  • FW_Validator

So, ich hoffe, dass der Aufbau der Klassen jetzt im groben verstanden wurde. Jetzt geht es an den Code. Der relevante Code Keine Angst, später habe ich natürlich noch den gesamten Code für euch. Aber hier erstmal das Wichtigste.

class FW_Form
{
  protected $elements = array();

  public function addElement(FW_Form_Element_Abstract $element, $position = null)
  {
    if($position === null || !is_int($position))
    {
      $this->elements[] = $element;
    }
    else
    {
      $this->elements[$position] = $element;
    }
    return $this;
  }

  public function getElementByPosition($position)
  {
    if(is_int($position) && isset($this->elements[$position]))
    {
      return $this->elements[$position];
    }
    else
    {
      throw new FW_Form_Exception("Diese Position hat einen ungültigen Wert");
    }
  }

  public function __toString()
  {
    $html = "
elements as $k => $element) { $html .= "\n ".$element->getHTML()."\n"; } $html .="
"; return $html; } }

Das ist jetzt sozusagen die Mutterklasse für alle Elemente. Mit der addElement-Methode kann man die eigentlichen Elemente zum Formular hinzufügen und gleichzeitig (optional) die Position bestimmen. Manche kennen das vielleicht schon von JavaScript: getElementById(). Ich habe hier diese Namenskonvention übernommen und eine Methode namens getElementByPosition() eingeführt. Mit dieser kann man später das Objekt, das mit addElement() hinzugefügt wurde, erhalten und bearbeiten. Hier die abstrakte Klasse FW_Form_Element_Abstract

abstract class FW_Form_Element_Abstract
{
  protected $validators = array();
  protected $decorators = array();

  public function addValidator(FW_Validator_Abstract $validator)
  {
    $this->validators[] = $validator;    
    return $this;
  }

  final public function getHTML()
  {
    $html = "";
    foreach($this->decorators as $decorator)
    {
      $html .= $decorator->getHTMlBefore($this);
    }
    $html .= $this->getHTMLofElement();
    foreach($this->decorators as $decorator)
    {
      $html .= $decorator->getHTMlAfter($this);
    }
    return $html; 
  }

  abstract public function getHTMLofElement();

  public function addDecorator(FW_Form_Decorator_Abstract $decorator, $amount = 1)
  {
    for($i = 0; $i < (int)$amount; $i++)
    {
      $this->decorators[] = $decorator;
    }  
    return $this;
  }
}

Die Methodennamen sprechen eigentlich für sich. Mit addValidator() kann man Validatoren hinzufügen und mit addDecorator() Decorators. Der Parameter $amount in addDecorator() gibt an, wie oft man den Decorator adden will. Die abstrakte Klasse FW_Form_Decorator_Abstract ist ganz klein und übersichtlich:

abstract class FW_Form_Decorator_Abstract
{
  public function getHTMLBefore(FW_Form_Element_Abstract $parentObject)
  {
    return "";  
  }

  public function getHTMLAfter(FW_Form_Element_Abstract $parentObject)
  {
    return "";
  }
}

Die beiden Methoden werden bei Bedarf von den eigentlichen Dekoratoren überschrieben. Jetzt kommen wir zu den Validatoren:

abstract class FW_Validator_Abstract
{
  protected $validators = array();
  protected $values = array();

  final public function isValid()
  {
    //Erst prüfen, ob die hinzugefügten Validatoren alle true zurückgeben...
    foreach((array)$this->validators as $validator)
    {
      //... wenn nicht, abbruch der Schleife und Rückgabe von false
      if(!$validator->isValid())
      {
        return false;
      }
    }

  //Wenn die bisherigen Validatoren alle true ergaben, entscheidet der letzte (der "Elternvalidator"),
  //ob die Prüfung true oder false ergibt.
   return $this->isThisValid();
  }

  final public function addValidator(FW_Validator_Abstract $validator)
  {
    $validator->setValues($this->values);
    $this->validators[] = $validator;
  }

  abstract public function isThisValid();

  final public function setValues(array $values = array())
  {
    $this->values = $values;
    //bei allen hinzugefügten validatoren werte mitteilen
    foreach((array)$this->validators as $validator)
    {
      $validator->setValues($this->values);
    }    
  }  

  final public function __construct(array $values = array())
  {
    try
    {
      $this->setValues($values);
    }
    catch(FW_Validator_Exception $e)
    {
      throw $e;
    }
    $this->init();
  }  

  public function init()
  {
  }
}

Man kann hier einen Baum von Validatoren erzeugen. Ob das sinnvoll ist oder nicht, kann ich jetzt noch nicht beurteilen. Ich habe die Funktion einfach mal eingebaut, auch wenn mir noch kein Verwendungszweck eingefallen ist. Kann man ja später immer noch entfernen! Jetzt hätten wir alle wichtigen Klassen zusammen. Ein Formular kann jetzt so erzeugt werden

$form = new FW_Form("register", "post", FW_Tools::getInternalUrl("user", "register2"));

Elemente fügt man so hinzu

$form->addElement(new FW_Form_Element_Text("username"), 1);

Bearbeiten kann man die Elemente so

$form->getElementByPosition(1)->setID("nickname")
                                  ->addDecorator(new FW_Form_Decorator_Break, 1)
                                  ->addDecorator(new FW_Form_Decorator_Label("Username"))
                                  ->addValidator(new FW_Validator_Length);

Und wenn man das Formular dann sehen will, nutzt man __call():

echo $form;

Aktuelle Version downlaoden Ihr könnt den momentanen Entwicklungsstatus einfach testen, indem ihr euch entweder

Probleme und Fragen Das ist ja jetzt alles schön und gut! Aber ich habe ein großes Problem bei dieser Lösung. Wenn man mal ein Beispielformular erstellt, wie ich es hier getan habe, sieht man gleich einen gewaltigen Schönheitsfehler:

public function register_Action()
  {     
    $form = new FW_Form("register", "post", FW_Tools::getInternalUrl("user", "register2"));
    $form->setParameter("id", "register");
    $form->addElement(new FW_Form_Element_Text("username"), 1);
    $form->addElement(new FW_Form_Element_Text("password1"), 2);
    $form->addElement(new FW_Form_Element_Text("password2"), 3);
    $form->addElement(new FW_Form_Element_Text("email"), 4);    
    $form->addElement(new FW_Form_Element_Select("auswahl"), 5);    
    $form->addElement(new FW_Form_Element_Submit("reg", "Anmelden"), 6);
    $form->addElement(new FW_Form_Element_Reset("res", "Reset"));
    $form->addElement(new FW_Form_Element_Button("but", "Ein Button"));
    $form->getElementByPosition(1)->setID("nickname")
                                  ->addDecorator(new FW_Form_Decorator_Break, 1)
                                  ->addDecorator(new FW_Form_Decorator_Label("Username"))
                                  ->addValidator(new FW_Validator_Length);
    $form->getElementByPosition(2)->setID("pw1")
                                  ->addDecorator(new FW_Form_Decorator_Break, 1)
                                  ->addDecorator(new FW_Form_Decorator_Label("Passwort"));
    $form->getElementByPosition(3)->setID("pw2")
                                  ->addDecorator(new FW_Form_Decorator_Break, 1)
                                  ->addDecorator(new FW_Form_Decorator_Label("Passwort wiederholen"));
    $form->getElementByPosition(4)->setID("mail")
                                  ->addDecorator(new FW_Form_Decorator_Break, 1)
                                  ->addDecorator(new FW_Form_Decorator_Label("E-Mail-Adresse"));
    $form->getElementByName("auswahl")->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addOption(new FW_Form_Element_Option("hallo", "lol"))
                                      ->addDecorator(new FW_Form_Decorator_Break, 2)
                                      ->setID("auswahl")
                                      ->setParameter("size", "1")
                                      ->addDecorator(new FW_Form_Decorator_Label("Auswahl"));
    $form->getElementByName("reg")->setID("loel")->addDecorator(new FW_Form_Decorator_Label("loll"));
    $this->view->addTemplateContent("content", array("errors" => null, "form" => $form), "user/register.tpl.php");
  }

Die Ausgabe wäre: haesslichesformular Na, was ist hier faul? Richtig! Die Eingabefelder sind nicht richtig schön untereinander. Abhilfe würden hier Tabellen schaffen, was aber beim derzeitigen Konzept nicht hineinpasst. Ich könnte zwar sagen, dass ich generell alle Labels in die linke Tabellenspalte setze und alle Eingabefelder in die rechte, aber was mache ich, wenn ich mal eine Ausnahme machen will? Das ganze wäre dadurch nichtmehr dynamisch und genau das will ich vermeiden. Habt ihr eine Idee?

1 Star2 Stars3 Stars4 Stars5 Stars (Wurde noch nicht bewertet)
Loading...


9 Kommentare zu “Formulare verarbeiten und überprüfen mit meinem Framework”

  1. Wie wäre es mit einem
        $form->setParameter(„class“, „form-label“);
    ?

    Also, dass die CSS-Klasse „form-label“ gesetzt wird und man das ganze Formular dann per CSS stylen kann.

  2. Bzgl. der Ausgabeformatierung:

    Wie wäre es mit einem View Helper?

    Beispiel:

    form(‚form_name‘) ?>
    htmlTag() ?>

    Name:
    name ?>

    Nachricht:
    message ?>

    submit ?>

  3. Das mit dem Beispiel war wohl nichts. 😀
    Siehe http://pastebin.com/m5cb58333

  4. Ja, da habt ihr recht!
    Vielen dank für die Kommentare.

    Ich war irgendwie die ganze Zeit auf dem Trip, dass ich alles über Tabellen regeln muss. Hatte auch länger nichts mehr mit HTML zu tun.

    Mal schauen, wann ich Zeit für die Implementierung eurer Ideen habe. Erstmal muss ich mir wieder ein wenig HTML-Formular-Wissen aneignen, denn damit hatte ich schon Ewigkeiten nichts mehr zu tun. Das hat bisher immer mein Designer für mich gemacht.

    Wie findet ihr die Klasse ansonsten?

    MfG Simon

  5. Ich verfolge die Einträge zum Framework (und nicht nur die) mit Spannung. Ich versuche mich gerade selbst in OOP einzuarbeiten und da ist es mehr als hilfreich, mal ein MVC-FW von Grund auf entstehen zu sehen.
    Ich kann nur sagen, danke und weiter so!

  6. Freut mich, wenn es dir gefällt 🙂
    Ich hoffe nur, dass ich in nächster Zeit wieder was dran machen kann. Jetzt sind die Ferien nämlich wieder vorbei

    Simon

  7. […] Ich verwende __toString() auch in meinem aktuellen Projekt (Formularvalidierung). […]

  8. […] Die letzte Version von FW_Form konnte noch nicht viel. Es war zwar möglich, Formulare zu generieren, aber das wars dann auch schon. Die neue und aktuelle Version kann Formulare jetzt überprüfen und Fehler anzeigen. Klar, es ist noch nicht alles 100%ig fertig und perfekt, aber ein Anfang ist es auf jeden Fall! […]

  9. Die aktuellste Version meines HMVC-Frameworks erhaltet ihr ab sofort immer hier: http://www.net-developers.de/blog/2011/02/13/download-info-shfw-hmvc-framework-in-php/

Hinterlasse einen Kommentar!

Time limit is exhausted. Please reload the CAPTCHA.

»Informationen zum Artikel

Autor: Simon
Datum: 28.02.2009
Zeit: 23:45 Uhr
Kategorien: Mein MVC-Framework
Gelesen: 10791x heute: 5x

Kommentare: RSS 2.0.
Diesen Artikel kommentieren oder einen Trackback senden.

»Meta