Request Router / Request Resolver meines Frameworks

Für mein aktuelles Projekt tempim benötige ich URLs, die mehr Parameter als nur Action und Controller in der URL aufnehmen können. Das wäre eigentlich kein Problem, denn ich könnte es ja so machen:

controller/action.html?weitere=parameter&so=weiter

Das ist aber nicht nur unschön, es vernichtet auch alle Vorteile, die ich durch mod_rewrite habe. Also suchte ich nach einer Möglichkeit, URLs wie

picture/2/thumb

picture/2/dateiname.endung

controller/irgendeinparameter/action

controller/

action/controller

zu realisieren. Das wäre mit mod_rewrite-Regeln ohne weiteres möglich. Aber ich möchte einen Schritt weitergehen und mein Framework mit dieser Aufgabe beauftragen. Meine Anforderungen an die Komponente sind/waren:

  • Beliebige Anzahl von Routen
  • Beliebige Reihenfolge der Parameter in den Routen
  • Statische Teile (Konstanten) in die URL einfügen
  • Vergabe von Regeln der Parameter (ID muss z.B. immer int sein, ein Dateiname benötigt eine Endung, usw…)
  • Dynamische Erstellung von URLs mithilfe eines ViewHelpers

Bis auf den letzten Punkt konnte ich alles meinen Vorstellungen gemäß verwirklichen. Die Dynamische Erstellung von URLs ist aber noch nicht so dynamisch wie ich es gerne hätte. Dazu aber später mehr!

Alle Anfragen an das Framework weiterleiten

Um alle Anfragen auf ein Verzeichnis (documentroot) an das Framework weiterzuleiten, verwende ich folgende Einträge in der .htaccess:

Options -MultiViews

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?query=$1 [QSA,L]

Die RewriteCond-Zeilen verhindern, dass real existierende Dateien durch das FW geroutet werden.

Die letzte Zeile weißt den Server an, alles, was im Request nach dem Document-Root steht, an das index.php-Skript unter $_GET[„query“] weiterzureichen. (Nur wenn die oberen Bedingungen  zutreffen) In der index.php muss dann nur noch festgelegt werden, wie die URLs geroutet werden.

Router festlegen

In der index.php reichen folgende beiden Zeilen aus, um modRewrite-gerecht zu routen:

$rRes = new FW_Http_Request_Resolver_ModRewrite($request);
$frontController->route($rRes);

Es wird ein Objekt des gewünschten Request-Resolvers erstellt und dem Front-Controller zur Verfügung gestellt. Intern beauftragt der Front-Controller dann den Router, die URL zu analysieren und die Parameter zur Verfügung zu stellen:

public function route(FW_Http_Request_Resolver_Abstract $rResolver = null)
 {
 if($rResolver == null)
 {
 $rResolver = new FW_Http_Request_Resolver_Standard($this->request);
 }    

 if(!$rResolver->parseRoutes())
 {
 $this->request->setActionName("Error");
 $this->request->setControllerName("error404");
 }

 //echo $rResolver->getActiveRoute();

 $this->requestResolver = $rResolver;
 $this->request->setRequestResolver($rResolver);
 }

Wie man sieht, muss kein Router übergeben werden. Wenn kein Router übergeben wird, dann wird der Standart-Request-Resolver verwendet. Dieser analysiert einfach normal-parameterisierte URLs wie index.php?controller=index&action=go

Wenn parseRoutes() false zurückgibt, wird eine Fehlerseite angezeigt. Man kann in diesem Stück Code lange danach suchen, wo jetzt eigentlich der Controller und die Action, sowie die weiteren Parameter zugewiesen werden. Man wird die Stelle nicht finden! Das ist nämlich nicht die Aufgabe des Front-Controllers, sondern die des Request-Routers. Der Router (=Resolver)  schreibt die entsprechenden Parameter dann in das Http_Request-Objekt, von dem aus der Front-Controller an die Daten kommt:

public function run()
{
 [...]
 $controllerName = $this->request->getControllerName();
 $actionName = $this->request->getActionName()."_Action";
 [...]
}

Ok, soviel zur Vorgeschichte. Jetzt kommen wir zum eigentlichen Thema dieses Artikels: Der Request-Resolver!

Die Funktionsweise des Request-Resolvers

Um sicherzustellen, dass auf jeden Request-Resolver genau gleich zugegriffen werden kann, verwende ich ein Interface. Dieses Interface bietet aber auch ein paar fertige Methoden, also ist es bei mir eine abstrakte Klasse:

abstract class FW_Http_Request_Resolver_Abstract
{
 protected $request = null;
 protected $routes = array();
 protected $active_route = "";

 public final function __construct(FW_Http_Request $request)
 {
 $this->request = $request;
 $this->init();
 }

 public function init()
 {
 //nix
 }

 final public function getActiveRoute()
 {
 return $this->active_route;
 }

 final public function getRequest()
 {
 return $this->request;
 }  

 public function addRoute($name, FW_Http_Request_Resolver_Route_Abstract $route)
 {
 $this->routes[$name] = $route;
 }

 public function getRoute($name)
 {
 $name = (string)$name;
 return isset($this->routes[$name]) ? $this->routes[$name] : null;
 }

 abstract public function parseRoutes();

}

Die Methode parseRoute muss von jedem Resolver deklariert werden.

Erstellung von Routen

Die Methode hat aber nichts zu tun, wenn keine Routen existieren. Deshalb folgt jetzt die abstrakte Klasse für die Routen! Davor aber noch ein kleiner Hinweis:

Es gibt die Möglichkeit, Konstanten in einer URL zu definieren. Diese Konstanten haben eine feste Position und einen unveränderlichen Wert. Möglich wäre also z.B. folgendes:

www.meinedomain.de/konstante/controller/action

www.meinedomain.de/controller/konstante/action

Um der Route klarzumachen, was eine Konstante ist und was nicht, habe ich folgende Regel aufgestellt: Alle Variablen beginnen mit einem @. Konstanten beginnen folglich nicht mit einem @. Jetzt aber zum Code!

abstract class FW_Http_Request_Resolver_Route_Abstract
{
 protected $route = "";
 protected $default_values = array();

 abstract public function isMatching($query);
 abstract function getParametersCount($only_dynamic = true);
}

Auch hier ist nicht viel zu sehen. Die Methode isMatching() prüft, ob die Route auf den Request passt. Die Methode getParametersCount() gibt die Anzahl der definierten Parameter (wahlweise mit oder ohne Konstanten) zurück.

Die Klasse für die ModRewrite-Routen sieht folgendermaßen aus.

class FW_Http_Request_Resolver_ModRewrite_Route extends FW_Http_Request_Resolver_Route_Abstract
{
 private $route_parts = array();
 private $validators  = array();

 public function __construct($route, array $default_values = array(), array $validators = array())
 {
 //Abspeichern der Route
 $this->route = (string)$route;

 // Slashes ("/") am Anfang und Ende der URL entfernen
 while(substr($this->route, 0,1) == "/" || substr($this->route, -1, 1) == "/")
 {
 if(substr($this->route, 0,1) == "/")
 {
 $this->route = substr($this->route, 1);
 }

 if(substr($this->route, -1, 1) == "/")
 {
 $this->route = substr($this->route, 0, -1);
 }
 }

 //Abspeichern der Standartwerte
 $this->default_values = $default_values;

 //Speichern der Validatoren
 $this->validators = $validators;

 foreach($this->validators as $field => $validators)
 {
 foreach($validators as $validator)
 {
 if(!$validator instanceof FW_Validator_Abstract)
 {
 throw new FW_Http_Request_Resolver_ModRewrite_Exception("Ungültiger Validator im Feld ".$field);
 }
 }
 }

 //Ermitteln der Teile der Route
 $this->route_parts = $this->getParts();
 }

 private function getParts()
 {
 //Aufteilen der Route anhand eines Slashs (Ergebnis = Array)
 $parts = explode("/", $this->route);

 //Zählvariable für die Position der Routenteile
 $pos = 0;

 $parsed_parts = array();

 //Durchlaufen aller Teile des Arrays
 foreach($parts as $key => $part)
 {
 if(empty($part))
 {
 //Element wird nicht übernommen
 }
 else
 {
 //Ist der Teil variabel oder konstant?
 if(substr($part, 0,1) == "@")
 {
 $parsed_parts[] = array(
 "variable" => true,            //ist Variabel
 "name" => substr($part, 1),    //Name ohne @
 "pos" => $pos                  //Position in Route
 );
 }
 else
 {
 $parsed_parts[] = array(
 "variable" => false,          //Konstante
 "name" => $part,              //Name <=> Wert
 "pos" => $pos                 //Position in Route
 );
 }
 }
 $pos++;  //Erhöhen der Zählvariable
 }

 //Rückgabe der Teile in Form eines Arrays
 return $parsed_parts;
 }

 private function extractParameters($query, $extract_all = false)
 {
 //Initialisierung des Arrays mit den extrahierten Parametern
 $params = array();

 //Query (meistens aus GET) anhand des Trennzeichens (/) aufteilen.
 $query_parts = $this->getQueryParts($query);    

 //Durchlaufen aller Teile der Route (NICHT query!)
 foreach($this->route_parts as $route_part)
 {
 //Wenn der Teil variabel ist oder auch Konstanten extrahiert werden sollen
 if($route_part["variable"] || $extract_all === true)
 {
 //Prüfen, ob der Teil der Route auch im Query vorhanden ist
 if(isset($query_parts[$route_part["pos"]])
 && !empty($query_parts[$route_part["pos"]]))
 {
 //Wenn ja, Wert aus der URL in $value ablegen
 $value = $query_parts[$route_part["pos"]];

 //------------------------------
 //TODO:
 //Entfernen der folgenden 3 Zeilen.
 //Man muss in der Route angeben können, ob .html erlaub ist
 //oder nicht
 //------------------------------
 if(substr($value, -5) == ".html")
 {
 $value = substr($value, 0, -5);
 }      

 //Die ermittelten Werte werden dem Array zugewiesen
 $params[$route_part["name"]] = $value;
 }
 }
 }

 //Rückgabe des Arrays
 return $params;
 }

 public function getParameters($query, $include_constants = false)
 {
 $params = array();
 foreach($this->default_values as $key => $value)
 {
 $params[$key] = $value;
 }
 $params = array_merge($params, $this->extractParameters($query));
 return $params;
 }

 private function getQueryParts($query)
 {
 //Auftrennen der URL anhand von "/"
 $query_parts = explode("/", $query);

 //Aufräumen (Entfernen von leeren Elementen)
 $query_parts = array_diff($query_parts, array(""));

 return $query_parts;
 }

 public function getParametersCount($only_dynamic = true)
 {
 $count = 0;
 foreach($this->route_parts as $part)
 {
 if($only_dynamic)
 {
 if($part["variable"])
 {
 $count++;
 }
 }
 else
 {
 $count++;
 }
 }

 return $count;
 }

 public function issetPart($name)
 {
 foreach($this->route_parts as $part)
 {
 if($name == $part["name"])
 {
 return true;
 }
 }
 return false;
 }

 public function isMatching($query)
 {
 //Query aufsplitten
 $query_parts = $this->getQueryParts($query);

 //Extrahieren aller Parameter ink. Konstanten (true)
 $query_values = $this->extractParameters($query, true);

 //Zuweisen der Routenteile in lokale Variable
 $route_parts = $this->route_parts;  

 //Anzahl der Anfrageparameter
 $cnt1 = count($query_parts);

 //Anzahl der Routenteile
 $cnt2 = count($route_parts);    

 //Standartrückgabewert
 $return = true;    

 //Hat die Route die gleiche Parameteranzahl wie die Query?
 if($cnt1 !== $cnt2)
 {
 $return = false;
 }
 else
 {
 //Durchlaufen der Routenteile
 foreach($route_parts as $route_value)
 {
 //Teil ist konstant??
 if(!$route_value["variable"])
 {
 //Jetzt wird geprüft, ob der Wert der Konstanten auch deren
 //Namen entspricht. Andernfalls ist die Route nicht die passende.
 if(!isset($route_value["name"])                 //Gibt es den Wert überhaupt?
 || !isset($query_values[$route_value["name"]])  // --"--
 || $query_values[$route_value["name"]] != $route_value["name"])
 {
 $return = false;
 }
 }
 //Teil ist variabel
 else
 {
 if(isset($this->validators[$route_value["name"]]))
 {
 //Abarbeiten der Validatoren
 foreach($this->validators[$route_value["name"]] as $validator)
 {
 $validator->setValue("to_check", $query_values[$route_value["name"]]);

 if(!$validator->isValid())
 {
 $return = false;
 }
 }
 }
 }
 }
 }

 return $return;
 }
}

Ganz schön viel Code, oder? Der hat mich auch jede Menge Zeit gekostet…

public function __construct($route, array $default_values = array(), array $validators = array())

public function __construct($route, array $default_values = array(), array $validators = array())
 {
 //Abspeichern der Route
 $this->route = (string)$route;

 // Slashes ("/") am Anfang und Ende der URL entfernen
 while(substr($this->route, 0,1) == "/" || substr($this->route, -1, 1) == "/")
 {
 if(substr($this->route, 0,1) == "/")
 {
 $this->route = substr($this->route, 1);
 }

 if(substr($this->route, -1, 1) == "/")
 {
 $this->route = substr($this->route, 0, -1);
 }
 }

 //Abspeichern der Standartwerte
 $this->default_values = $default_values;

 //Speichern der Validatoren
 $this->validators = $validators;

 foreach($this->validators as $field => $validators)
 {
 foreach($validators as $validator)
 {
 if(!$validator instanceof FW_Validator_Abstract)
 {
 throw new FW_Http_Request_Resolver_ModRewrite_Exception("Ungültiger Validator im Feld ".$field);
 }
 }
 }

 //Ermitteln der Teile der Route
 $this->route_parts = $this->getParts();
 }

Der Konstruktor nimmt die Route entgegen, nimmt Standartwerte an und fügt Validatoren hinzu. Die Standartwerte und Validatoren sind dabei optional.
Am Schluss des Konstruktors wird dann noch $this->getParts() aufgerufen, um die Route zu analysieren und die einzelnen Teile zu erhalten.

private function getParts()

private function getParts()
 {
 //Aufteilen der Route anhand eines Slashs (Ergebnis = Array)
 $parts = explode("/", $this->route);

 //Zählvariable für die Position der Routenteile
 $pos = 0;

 $parsed_parts = array();

 //Durchlaufen aller Teile des Arrays
 foreach($parts as $key => $part)
 {
 if(empty($part))
 {
 //Element wird nicht übernommen
 }
 else
 {
 //Ist der Teil variabel oder konstant?
 if(substr($part, 0,1) == "@")
 {
 $parsed_parts[] = array(
 "variable" => true,            //ist Variabel
 "name" => substr($part, 1),    //Name ohne @
 "pos" => $pos                  //Position in Route
 );
 }
 else
 {
 $parsed_parts[] = array(
 "variable" => false,          //Konstante
 "name" => $part,              //Name <=> Wert
 "pos" => $pos                 //Position in Route
 );
 }
 }
 $pos++;  //Erhöhen der Zählvariable
 }

 //Rückgabe der Teile in Form eines Arrays
 return $parsed_parts;
 }

Als erstes wird die Route an allen / zerstückelt. So erhält man die Parameter, die sich in der Route befinden. Danach werden alle diese Teile durchlaufen und geprüft, ob sie variabel oder konstant sind. Eine Variable bekommt immer ein @ vorgestellt, eine Konstante ist einfach nur Text. Wichtig ist, dass die Position des jeweilen Routenteils mit abgespeichert wird. Nur so kann sichergestellt werden, dass sich alles an der richtigen Stelle befindet.

private function extractParameters($query, $extract_all = false)

private function extractParameters($query, $extract_all = false)
 {
 //Initialisierung des Arrays mit den extrahierten Parametern
 $params = array();

 //Query (meistens aus GET) anhand des Trennzeichens (/) aufteilen.
 $query_parts = $this->getQueryParts($query);    

 //Durchlaufen aller Teile der Route (NICHT query!)
 foreach($this->route_parts as $route_part)
 {
 //Wenn der Teil variabel ist oder auch Konstanten extrahiert werden sollen
 if($route_part["variable"] || $extract_all === true)
 {
 //Prüfen, ob der Teil der Route auch im Query vorhanden ist
 if(isset($query_parts[$route_part["pos"]])
 && !empty($query_parts[$route_part["pos"]]))
 {
 //Wenn ja, Wert aus der URL in $value ablegen
 $value = $query_parts[$route_part["pos"]];

 //------------------------------
 //TODO:
 //Entfernen der folgenden 3 Zeilen.
 //Man muss in der Route angeben können, ob .html erlaub ist
 //oder nicht
 //------------------------------
 if(substr($value, -5) == ".html")
 {
 $value = substr($value, 0, -5);
 }      

 //Die ermittelten Werte werden dem Array zugewiesen
 $params[$route_part["name"]] = $value;
 }
 }
 }

 //Rückgabe des Arrays
 return $params;
 }

Hier werden dann die tatsächlichen Werte aus der URL ausgelesen. $extract_all gibt an, ob nur Variablen oder auch Konstanten ausgelesen werden sollen. Innerhalb der Methode wird dann ein Array mit allen Parametern, die zurückgegeben werden sollen, angelegt.

public function isMatching($query)

Diese Methode hat mich verdammt viel Zeit und Nerven gekostet. Doch jetzt funktioniert sie einwandfrei 🙂

public function isMatching($query)
 {
 //Query aufsplitten
 $query_parts = $this->getQueryParts($query);

 //Extrahieren aller Parameter ink. Konstanten (true)
 $query_values = $this->extractParameters($query, true);

 //Zuweisen der Routenteile in lokale Variable
 $route_parts = $this->route_parts;  

 //Anzahl der Anfrageparameter
 $cnt1 = count($query_parts);

 //Anzahl der Routenteile
 $cnt2 = count($route_parts);    

 //Standartrückgabewert
 $return = true;    

 //Hat die Route die gleiche Parameteranzahl wie die Query?
 if($cnt1 !== $cnt2)
 {
 $return = false;
 }
 else
 {
 //Durchlaufen der Routenteile
 foreach($route_parts as $route_value)
 {
 //Teil ist konstant??
 if(!$route_value["variable"])
 {
 //Jetzt wird geprüft, ob der Wert der Konstanten auch deren
 //Namen entspricht. Andernfalls ist die Route nicht die passende.
 if(!isset($route_value["name"])                 //Gibt es den Wert überhaupt?
 || !isset($query_values[$route_value["name"]])  // --"--
 || $query_values[$route_value["name"]] != $route_value["name"])
 {
 $return = false;
 }
 }
 //Teil ist variabel
 else
 {
 if(isset($this->validators[$route_value["name"]]))
 {
 //Abarbeiten der Validatoren
 foreach($this->validators[$route_value["name"]] as $validator)
 {
 $validator->setValue("to_check", $query_values[$route_value["name"]]);

 if(!$validator->isValid())
 {
 $return = false;
 }
 }
 }
 }
 }
 }

 return $return;
 }

isMatching() prüft, ob die Route auf die übergebene Anfrage ($query) passt. Das geschieht folgendermaßen

  • Prüfen, ob die Anfrage gleichviele Teile hat wie die Route. Wenn nein => passt nicht
  • Route durchlaufen
    • Ist der Teil eine Konstante?
      • Der Wert dieses Parameters muss exakt der Konstante entsprechen. Wenn nicht => Route passt nicht
    • Ist der Teil eine Variable?
      • Gibt es Validatoren?
        • Durchlaufen aller Validatoren
          • Gibt der Validator false zurück? => Route passt nicht
  • Wenn keine Fehler auftraten, wird true zurückgegeben (Route passt), ansonsten false (Route passt nicht)

Der Request-Resolver für ModRewrite

Die lezte Komponente, die noch erklärt werden muss, ist der eigentliche Request-Resolver, der mod_rewrite sozusagen simuliert.

class FW_Http_Request_Resolver_ModRewrite extends FW_Http_Request_Resolver_Abstract
{
  public function init()
  {
    $this->addRoute("home",     new FW_Http_Request_Resolver_ModRewrite_Route("", array("controller" => "index", "action" => "index")));
    $this->addRoute("ctrl",     new FW_Http_Request_Resolver_ModRewrite_Route("@controller", array("action" => "index")));
    $this->addRoute("ctrl_act", new FW_Http_Request_Resolver_ModRewrite_Route("@controller/@action"));
  }

  public function parseRoutes()
  {
    $query = $this->request->getGet("query");

    $routes = array_reverse($this->routes);

    foreach($routes as $routename => $route)
    {
      if($route->isMatching($query))
      {
        $result = $route->getParameters($query);        

        foreach($result as $key => $value)
        {
          $this->request->setParam($key, $value);
        }
        $this->active_route = $routename;
        return true;
      }
    }
    return false;
  }
}

Die init()-Methode legt Standartrouten fest:

  • home: Wenn die Domain direkt aufgerufen wird, wird die Startseite angezeigt
  • ctrl: Wenn nur ein Controller übergeben wird, ist die Action „index“
  • ctrl_action: Wenn ein Controller und eine Action übergeben werden, werden die übergebenen Werte übernommen.

parseRoutes() geht alle angelegten Routen in umgekehrter Reihenfolge durch (das ist wichtig, weil allgemeine Routen zuerst definiert wurden, aber als letztes berücksichtigt werden sollen).

Wenn isMatching() der Route true zurückgibt, werden die Parameter extrahiert und dem Http_Request-Objekt zugewiesen:

$this->request->setParam($key, $value);

Das waren jetzt die wichtigsten Komponenten des FW_Http_Request_Resolvers. Um euch zu zeigen, wie die Klasse verwendet wird, kommt jetzt noch Beispielcode.

Beispielcode

Für Tempim benutze ich momentan diese Routen:

$rRes->addRoute("pic", new FW_Http_Request_Resolver_ModRewrite_Route("picture/@id/@name",
array(
"action" => "show",
"controller" => "picture"
),
array("id" => array(
new FW_Validator_Type_Integer()
)
)
)
);

$rRes->addRoute("picthmb", new FW_Http_Request_Resolver_ModRewrite_Route("picture/thumb/@id/@name",
array(
"action" => "show_thumb",
"controller" => "picture"
),
array("id" => array(
new FW_Validator_Type_Integer()
)
)
)
);

$rRes->addRoute("picdetails", new FW_Http_Request_Resolver_ModRewrite_Route("picture/details/@id/@name",
array(
"action" => "show_details",
"controller" => "picture"
),
array("id" => array(
new FW_Validator_Type_Integer()
)
)
)
);

$rRes->addRoute("links", new FW_Http_Request_Resolver_ModRewrite_Route("picture/links/@id",
array(
"action" => "show_links",
"controller" => "picture"
),
array("id" => array(
new FW_Validator_Type_Integer()
)
)
)
);

$rRes->addRoute("impressum", new FW_Http_Request_Resolver_ModRewrite_Route("impressum", array("controller" => "Help", "action" => "impressum")));

Hier kann man sehen, wie Validatoren und Standartwerte verwendet werden.

Verwendung von Standartwerten

Wenn man z.B. eine Seite hat, die das Impressum anzeigt, aber die Methode zum Anzeigen des Impressums in der Klasse „Help“ liegt, würde die URL so heißen:

/help/impressum.

Da das nicht besonders sinnvoll ist, möchte man vllt. so eine URL:

/impressum/

Um jeztt nicht extra einen Controller fürs Impressum anlegen zu müssen, kann man es so machen wie ich:

$rRes->addRoute(„impressum“, new FW_Http_Request_Resolver_ModRewrite_Route(„impressum“, array(„controller“ => „Help“, „action“ => „impressum“)));

Hier wird eine Route definiert, die nur aus einer Konstante („impressum“) besteht. Der Controller und die Action werden im Array für die Standartwerte definiert. Schon wird beim Aufruf von /impressum/ das Impressum angezeigt!

Verwendung von Validatoren

Als dritter Parameter einer Route kann ein Array mit Validatoren übergeben werden. Das ist so aufgebaut:

array(„parametername1“ => array(validatoren…), „parametrname2“ => array(validatoren…))

So kann man zu beliebig vielen Elementen beliebig viele Validatoren hinzufügen. Meine Route „picdetails“ z.B. wird nur als passend erachtet, wenn die übergebene ID auch tatsächlich nur aus Zahlen besteht.

Zugriff auf Parameter innerhalb eines Controllers

Um die Parameter auch nutzen zu können, kann man in jedem Controller einfach schreiben

$id =   $this->request->getParam(„id“);

Die Problematik mit dem dynamischen Erstellen der URLs

Meine Idee war, dass sich ein ViewHelper die passende Route anhand von übergebenen Parametern heraussucht. Das Problem ist aber, dass bei folgenden Routen immer alle passen würden:

  • /@controller/@action
  • /@controller/trenner/@action
  • /prefix/@controller/@action

Welche Route soll ich nehmen, wenn ich meinem ViewHelper Controller und Action übergebe?

An dieser Problematik bin ich gescheitert. Es gäbe zwar die Möglichkeit, dass ich dem ViewHelper sage, welche Route er zur Erstellung der URLs nehmen soll, aber dann wäre wieder alles gekoppelt aneinenader. Deshalb funktioniert mein URL-View-Helper jetzt so:

public static function getInternalURL($controller = null, $action = null, array $additional_params = array(), $extention = "")
{
$request = FW_Http_Request::getInstance();

$params = array_merge(array("controller" => $controller !== null ? $controller : "index",
"action" => $action
),
$additional_params);

$url = FW_Config::getInstance()->get("www_root");

$c = 0;

foreach($params as $name => $value)
{
if(FW_Config::getInstance()->get("mod_rewrite") == "1")
{
$url .= "/".$value;
}
else
{
if($c == 0)
{
$url .= "?".$name."=".$value;
}
else
{
$url .= "&".$name."=".$value;
}
}

$c++;
}

$url .= $extention;

return $url;
}

Es werden einfach stumpf alle übergebenen Parameter aneinandergereiht. Dabei wird immer darauf geachtet, dass der Controller vor der Action kommt und dann die restlichen Parameter. Mit der Lösung bin ich überhaupt nicht zufrieden. Wie hättet ihr das gemacht?

Bei Fragen zu diesen zugegebenermaßen sehr langen Artikel dürft ihr gerne die Kommentarfunktion benutzen 😉

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


4 Kommentare zu “Request Router / Request Resolver meines Frameworks”

  1. […] FW_Http_Request_Resolver: Diese Klasse hat mich eine Menge Arbeit gekostet. Jetzt kommt sich aber schon fast an die Funktionalität von Zend_Route ran Mit dieser Klasse kann man festlegen, bei welchen URLs was geschieht. “mod_rewrite für PHP” also. […]

  2. Ich verstehe immer noch nicht den Sinn wieso du ein eigenes Framework
    schreibst, d.h. das Rad zum 1000. neu erfindest?!

  3. Weil dieses Framework genau meinen Anforderugen gewachsen ist und genau so arbeitet, wie ich es will.

    Ich muss mich weder mit der Syntax anderer FWs anfreunden, noch ewig Dokus lesen, noch passende Plugins für diese FWs suchen, sondern kann direkt loslegen, so wie es mir am besten gefällt.

    So ganz nebenbei lerne ich auch enorm viel über MVC, OOP und diverse Patterns. Der einzige Nachteil, den es gibt, ist dass es länger dauert, als ein fertiges FW zu benutzen.

    Da ich aber zeitlich nicht eingeschränkt bin, macht das überhaupt nichts aus 🙂

    MfG
    Simon

  4. 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: 07.11.2009
Zeit: 14:48 Uhr
Kategorien: Mein MVC-Framework
Gelesen: 18370x heute: 4x

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

»Meta