Mein eigenes MVC-Framework: Die View-Helper

<< Zurück zur Übersicht

Es gibt immer wieder Dinge, die man an mehreren Stellen benötigt, z.B. das Formatieren der Uhrzeit. Um diese Funktionen nicht immer wieder neu schreiben zu müssen, gibt es sogenannte ViewHelper. Wie der Name schon sagt, kommen diese in der View-Schicht zum Einsatz. Das View-Helper-Pattern ist also ein „Unterpattern“ des MVC-Patterns in der View-Schicht.

ViewHelper und ViewDataHelper

In meinem Framework gibt es außer den View-Helpern noch View-Data-Helper, die außerdem in der Lage sind, Daten zu speichern und auszulesen. Der Aufruf dieser Helper funktioniert aber genau wie der Aufruf eines einfachen View-Helpers. Auch die Implementierung erfolgt genau gleich. Alle Helper (Egal ob View oder ViewData) müssen nur das Interface FW_View_Helper_Interface implementieren. Dieses Interface sieht so aus:

Das Interface: FW_View_Helper_Interface

interface FW_View_Helper_Interface
{
  public function run(array $params = array());
}

Es wird also nur die run-Methode benötigt, die alle Parameter entgegennimmt.

Da wir wollen, dass man alle ViewHelpers und ViewDataHelpers möglichst einfach aufrufen kann, eignet sich hier die __call()-Interzeptormethode.

Wir wollen aber nicht immer den kompletten Namen der Klasse schreiben, sondern einfach nur FW_ViewHelperName statt FW_View_Helper_ViewHelper_Name. Ist ein Helper kein Teil des Frameworks, sondern eine Eigenkreation, lassen wir einfach das FW_-Prefix weg. Also ViewHelperName.

Modifizierungen in der abstrakten View-Klasse

Die bereits vorhandene Klasse FW_View_Abstract, von der alle View-Klassen ableiten müssen, muss jetzt um die __call-Methode erweitert werden. Folgender Code erfüllt alle oben genannten Anforderungen.

/**
    * @access public
    * @param string $func
    * @param array  $params
    *
    * Aufruf von ViewHelpers.
    * Wenn der Name des helpers mit FW_ beginnt, ist der Helper ein Teil des
    * Frameworks. Deshalb wird FW_View_Helper_ als Prefix benutzt.
    * Ansonsten ist das Prefix einfach ViewHelper.
    */
   public function __call($func, $params)
   {
       if(substr($func, 0, 3) == "FW_")
       {
         $class = "FW_View_Helper_".substr($func, 3);
       }
       else
       {
         $class = "ViewHelper_".$func;
       }

       if(!isset($this->viewHelpers[$class]))
       {
         $this->viewHelpers[$class] = new $class;
       }       

       return $this->viewHelpers[$class]->run($params);
   }

Wie man sieht, wird von jedem ViewHelper immer nur jeweils eine Instanz erstellt. Das spart Ressourcen und ist zudem auch noch schneller. Außerdem muss man sich nicht selbst darum kümmern, ob man die richtige Instanz erhält.

Ein einfacher ViewHelper

Bevor wir unseren ersten ViewDataHelper schreiben, sollte uns erstmal das Prinzip des Aufrufs eines „normalen“ Helpers vertraut werden. Dazu schreiben wir einen kleinen Helper, der die aktuelle Uhrzeit zurückgibt.

class FW_View_Helper_Time implements FW_View_Helper_Interface
{

  public function run(array $parameters = array())
  {
    return date("H:i - d.m.Y", time());
  }
}

Das wars schon! Wir können ab jetzt ganz einfach an jeder Stelle in unseren Templates das aktuelle Datum ausgeben lassen. Dazu können wir einfach

<?=$this->FW_Time(); ?>

in ein Template schreiben.

Wenn wir wollen, dass der ViewHelper nicht im Frameworksordner ist, sondern im Projektordner, können wir ihn einfach in den Ordner classes/ViewHelper im Framework verschieben. Danach muss nur der Name von FW_View_Helper_Time in ViewHelper_Time geändert werden.

Eine erweiterte Version des Time-View-Helpers könnte z.B. so aussehen: (Diesmal ohne das FW_Prefix –> Projektordner)

class ViewHelper_Time implements FW_View_Helper_Interface
{
  public function run(array $params = array())
  {
    $timestamp = (int)(isset($params[1])) ? $params[1] : time();
    $pattern_reqired = (isset($params[0])) ? $params[0] : null;
    switch($pattern_reqired)
    {
      case "time":      $pattern = "H:i"; break;
      case "fulltime":  $pattern = "H:i:s"; break;
      case "date":      $pattern = "d.m.y"; break;
      case "full":      $pattern = "d.m.y - H:i:s"; break;
      default:          $pattern = "d.m.y - H:i"; break;
    }

    return date($pattern, $timestamp);
  }
}

Man kann bei dieser Version angeben, in welchem Format man das Datum haben möchte. Weiterhin kann man auch noch einen Timestamp mit angeben. z.B. so:

Vor einer Minute war es <?=$this->Time("full", time()-60); ?>

Es wird jetzt das volle Datum von vor einer Minute ausgegeben:

Vor einer Minute war es 24.06.09 – 10:11:37

ViewDataHelper

Man möchte in seinen Layout-Templates so gut wie immer einen passenden Seitentitel ausgeben. Aber wir macht man das am elegantesten? Woher holt sich das Layout den Titel? Aus der Registry?

Nein, es geht viel besser! Mit ViewDataHelpers! Wie oben schon erwähnt wurde, sind ViewDataHelpers in der Lage, Daten zu speichern und auszulesen. Intern bedienen sie sich dabei (zumindest in meinen Beispielen) an einer Registry mit eigenem Namespace.

Die Daten kommen über die Controller bzw. SubController in die Registry (Auch die Controller können auf die ViewDataHelpers zugreifen)

Dazu müssen wir erstmal unsere abstrakte FW_Controller_Base-Klasse um eine Methode erweitern, die den Zugriff ermöglicht. Auch hier mache ich mir wieder __call() zunutze.

 /**
  * @access protected
  * @param string $method
  * @param array  $parameters
  *
  * Wenn eine undefinierte Methode angefordert wird, kommt __call ins spiel
  * Sie wird hier im FW_Controller_Base definiert, weil sowohl FW_Controller als
  * auch FW_SubController diese Methode benötigen.
  * Man kann hier sogenannte ViewDataHelper aufrufen. (z.B. für Title, Meta,
  * Stylesheets,...)
  */
  protected function __call($method, $parameters)
  {
    if(!isset($this->ViewDataHelpers[$method]))
    {
      $this->ViewDataHelpers[$method] = new $method();
    }

    return $this->ViewDataHelpers[$method]->run($parameters);

  }

Lasst euch vom Namen des Arrays nicht iritieren. Ich habe es so genannt, weil man eigentlich nur ViewDataHelpers darin ablegt (Normale Helper machen dort normalerweise keinen Sinn). Trotzdem kann man auch ViewHelpers darin speichern.

Da alle Controller und SubController von FW_Controller_Base ableiten, ist es problemlos möglich, die ViewDataHelpers von überall aus anzusprechen (Ausgenommen sind hier die Models)

ViewHelper für Seitentitel

Ein Beispiel-Helper zum Speichern des Seitentitels sieht so aus:

class FW_View_Helper_Title implements FW_View_Helper_Interface
{
  const REGISTRY_KEY = "___FW__TITLE_TAG__REG";

  private $registry;

  public function run(array $parameters = array())
  {
    $this->registry = FW_Registry::getInstance(self::REGISTRY_KEY);
    return $this;
  }

  public function set($title)
  {
    $this->registry->title = $title;
  }

  public function get($title)
  {
    return (string)$this->registry->title;
  }

  public function __toString()
  {
    return "\n<title>".$this->registry->title."</title>\n";
  }
}

Am besten zeige ich gleich, wie das ganze angewendet wird, dann wird es klarer. Einfach an der Stelle, an der man den Titel setzen will, folgendes schreiben:

$this->FW_View_Helper_Title()->set("Mein MVC-Framework");

Durch den Aufruf von

$this->FW_View_Helper_Title()

Wird eine Instanz von FW_View_Helper_Title() zurückgegeben. Diese Instanz kann dann dank eines sogenannten Fluent Interface angesprochen werden. In diesem Fall setzen wir den Titel mit ->set(„Mein MVC-Framework“).

Im Template (Layout) kann dann der Titel wieder so ausgelesen werden:

<?=$this->FW_Title(); ?>

Aber jetzt zur Funktionsweise der Klasse:

Die run()-Methode legt eine Registry mit eigenem Namespace an und speichert sie in einem Attribut ab. Danach gibt sie $this zurück, was das Fluent Interface ermöglicht.

Wird jetzt – wie im Layout – das Objekt ausgegeben, ruft PHP automatisch __toString() auf. __toString() gibt dann den Titel fertig verpackt im title-Tag zurück.

Wird das Objekt nicht ausgegeben, sondern eine weitere Methode, wie z.B. set() aufgerufen, wird der Titel gesetzt.

Ich hoffe, dass das Prinzip hier klar wurde! Bei Fragen sind Kommentare wie immer erwünscht. Auch Anregungen zu Verbesserungen sind immer willkommen.

1 Star2 Stars3 Stars4 Stars5 Stars (3 Stimme, durchschnittlich 3,33 / 5)
Loading...


4 Kommentare zu “Mein eigenes MVC-Framework: Die View-Helper”

  1. […] Mein eigenes MVC-Framework: Die View-Helper […]

  2. 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/

  3. Moin Simon,

    ich verstehe nicht wo ich die ViewHelper hinpacken soll. Im Ordner Framework im Ordner ViewHelper und dann time.class.php?

    Irgendwie hänge ich hier.

    Beste Grüße

  4. Beziehst Du dich auf die aktuellste erhältliche Version des Frameworks?

    Dort gibt es einen seperaten Ordner für ViewHelpers und da kommen die Helper rein. Wo genau hängst du?

    Prinzipiell lässt sichd er Dateiname aus dem Namen der Klasse ableiten, somit kommt beispielsweise ViewHelper_Time in den Ordner ViewHelper\Time.class.php

    In einer älteren Version gab es dort eine besonderheit, nämlich dass der ViewHelper-Ordner einen anderen Namen trug. Leider habe ich diese Version selbst nicht mehr.

    Ich hoffe, ich konnte dir wenigstens ein bisschen weiterhelfen!

Hinterlasse einen Kommentar!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.

»Informationen zum Artikel

Autor: Simon
Datum: 24.06.2009
Zeit: 10:34 Uhr
Kategorien: Mein MVC-Framework
Gelesen: 21836x heute: 2x

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

»Meta