Verständnisfragen - Frontcontrolller im MVC Pattern

Davicito

Erfahrenes Mitglied
Liebe Community,

ich würde gerne zu meinem kleinen MVC-Übungsprojekt einen FrontController hinzufügen und hab mir auch ein paar Beispiele angesehen.
und so wie ich die Sache verstanden haben, bindet der FrontContoller eine/die Unterklasse ein und führt dessen ausführbare Methode (Action) aus.
Dazu habe ich zwei Fragen:

1. In einigen Beispielen wird gezeigt, das sich der Fronentcontroller selber instantiiert. Aber Wieso? Ich meine die Instantiierung des FC wird
doch in der Bootstrap-Datei " index.php" gemacht, oder nicht? Wo liegt der Vorteil, wenn ich es so mache wie in den unteren Beispielen?

Beispiel aus einem Froncontroller
PHP:
// Der Front Controller wird als Singleton Pattern implementiert, da wir ja nur eine Instanz pro Request haben wollen.
    public static function getInstance($rootPath = null) {
        // Falls es noch keine Instanz des FrontControllers gibt, wird hier eine instanziert.
        if (! is_object(self::$instance)) {
            if (is_null($rootPath)) {
                    throw new Exception('Es muss der Rootpfad angegeben werden!');
            }
            // Instanzieren des Front Controllers 
            self::$instance = new FrontController($rootPath);
        }
        // Instanz des FrontControllers zurück geben.

Selbst das Zend-FW macht es so:
PHP:
   /**
   * Singleton instance
   *
   * @return Zend_Controller_Front
   */
   public static function getInstance()
   {
   if (null === self::$_instance) {
      self::$_instance = new self();

   }
   return self::$_instance;
}

Ich meine ich kann mir das nur erklären, das ich nicht extern extra mit einem new-Operator arbeiten muss und einfach nur schreibe Klasse::getInstance(); und die Klasse selber instantiiert sich selbst. Ist ja auch statisch. Hab ich jedenfalls noch nie so gesehen!

2. Wenn mein FrontController nun die Unterklasse geladen hat und ruft dessen Aktion - zB. run() - auf, wird die Unterklasse ausgeführt und wenn diese fertig ist, woher weiß der FrontController, dass er jetzt eine andere Unterklasse - ich sag mal Modul - aufrufen kann/soll.

ZB. soll nach einer erfolgreichen Authentifizierung (Unterklasse: AuthentClass) auf die Homeseite geleitet werden und der Benutzer will einen Blog schreiben. Dann müsste doch der FC den UnterController, für das Erstellen von Blogs, aufrufen?
Muss der UnterController "AuthentClass" dem FrontController mitteilen, das er fertig ist? Oder wartet der FrontController auf ein Request vom Benutzer, um zu prüfen, was danach geschehen soll?

Auf Hilfe von Euch bin ich wie immer sehr erfreut und danke Euch schon mal im Voraus.

LG, Davicito.
 
Zuletzt bearbeitet:
Das mit dem selbst instanziieren hängt von der Implementierung des Frontcontrollers ab, in diesem Falle (ZF) wird er anscheinend als ein Singleton implementiert: http://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)
Ob das nun gut oder schlecht ist, da gibt's einige Meinungen zu im Internet. Lies mal den wikipedia Artikel, da sind die Vor- und nachteile eines Singltons gut augelistet.
Zu dem Wieso: Nunja, damit kann man sicherstellen dass es den Frontcontroller nur einmal gibt.

Normalerweise hat der FC auch garnix zu entscheiden, der delegiert die Aufgaben schön an andere Teile der Applikation weiter. Die Hauptaufgabe sehe ich hier in der Instanziierung des Routers sowie evt in den Instanziierungen des Abstraktionsobjekts für den Request. (Weiß ich aber nicht genau, hab noch nie nen framework selber geschrieben)
 
Zuletzt bearbeitet:
Hallo alxy,

vielen Dank für deine schnelle Antwort. Ich hab mir den Artikel auf Wikipedia durchgelesen und mir ist klarer geworden wofür man ein Singelton einsetzt. Dennoch finde ich das auch irgenwie sehr komisch, weil es der OOP sehr widerspricht und man vielleicht wirklich nur aus Performancegründen zu solch merkwürdigen Pattern greift. Nun ja!

Was die Funktionsweise eines FronControllers betrifft, denke ich auch das dieser nur deligiert. Aber wie es das macht, Hätte ich gerne mal ausführlicher gewust um mir mal ein Bild darüber zu machen. Wenn jetzt nicht der Untercontroller dem FC mitteilt, dass er fertig ist, um den nächsten UnterController aufzurufen, muss es doch irgendwie über den Request "POST" bzw. "GET" geregelt werden.
Den Request bekommt der FC doch nur durch die URL oder beim Absenden eines Formulars mitgeteilt.

In einem Beispiel wird dem FC ein Array, als Parameter, übergeben und verarbeitet diese um zu wissen was er für einen UnterController ausführen soll

1. Beispiel:
PHP:
 // Die run Methode wird bei jedem Request aufgerufen.
    public function run() {
        // Hier die aufgerufene Action aus dem Request gelesen.
        $completeName = $_REQUEST['action'];
        
        // In diesem Codeblock wird geprüft, ob eine Action übergeben/aufgerufen wurde oder nicht.
        // Wenn keine Action übergeben/aufgerufen wurde, wird die First Action ausgeführt.
        try {
            if (! empty($completeName)) {
                $this->executeAction($completeName);
            } else {
                $this->executeAction($this->firstAction);
            }
        } catch (Exception $e) {
            echo $e->getMessage();
        }
    }

2.Beispiel - Kompletter Froncontroller
PHP:
class FrontController implements FrontControllerInterface
{
    const DEFAULT_CONTROLLER = "IndexController";
    const DEFAULT_ACTION     = "index";
     
    protected $controller    = self::DEFAULT_CONTROLLER;
    protected $action        = self::DEFAULT_ACTION;
    protected $params        = array();
    protected $basePath      = "mybasepath/";
     
    public function __construct(array $options = array()) {
        if (empty($options)) {
           $this->parseUri();
        }
        else {
            if (isset($options["controller"])) {
                $this->setController($options["controller"]);
            }
            if (isset($options["action"])) {
                $this->setAction($options["action"]);     
            }
            if (isset($options["params"])) {
                $this->setParams($options["params"]);
            }
        }
    }
     
    protected function parseUri() {
        $path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), "/");
        $path = preg_replace('/[^a-zA-Z0-9]//', "", $path);
        if (strpos($path, $this->basePath) === 0) {
            $path = substr($path, strlen($this->basePath));
        }
        @list($controller, $action, $params) = explode("/", $path, 3);
        if (isset($controller)) {
            $this->setController($controller);
        }
        if (isset($action)) {
            $this->setAction($action);
        }
        if (isset($params)) {
            $this->setParams(explode("/", $params));
        }
    }
     
    public function setController($controller) {
        $controller = ucfirst(strtolower($controller)) . "Controller";
        if (!class_exists($controller)) {
            throw new InvalidArgumentException(
                "The action controller '$controller' has not been defined.");
        }
        $this->controller = $controller;
        return $this;
    }
     
    public function setAction($action) {
        $reflector = new ReflectionClass($this->controller);
        if (!$reflector->hasMethod($action)) {
            throw new InvalidArgumentException(
                "The controller action '$action' has been not defined.");
        }
        $this->action = $action;
        return $this;
    }
     
    public function setParams(array $params) {
        $this->params = $params;
        return $this;
    }
     
    public function run() {
        call_user_func_array(array(new $this->controller, $this->action), $this->params);
    }
}

Ist meine Annahme richtig, das der FC also nur auf Requests reagiert und dann den passenden UnterController läd, um dessen Aktion auszuführen?

Ich verstehe diesen Code nicht genau 100%, sonst hätte ich diesen in meiner app umsetzten können.
Wenn der FC also nur auf Request hörscht, die ja schließlich superglobale Variablen beinhalten, dann weiß ich noch nicht, wie er diiesen bekommen, wenn ich zB. mit Templates arbeiten möchte.

Wie im Eingang erwähnt, soll der FC den Untercontroller "AuthentClass" zur Benutzerauthentifizierung aufrufen und wenn der Benutzer angemeldet ist, soll dieser auf eine Homeseite weitergeleitet werden. Das mach alles der AuthentClassController.
Jetzt schreib Jemand auf dieser Homeseite (Template-Seite) einen Blog und klick auf "speichern" damit wird ein Formular, als Request an den Server geschickt und bekommen superglobalen Requestsalat ^^. Ist es also so, dass er dann auf diesen Salat hörscht um in weiterzuverarbeiten? Also das er weiß, dass ein BlogFormular abgesendet wurde und daher dann den BlogController aufruft?

Gruß.
 
Zuletzt bearbeitet:
OK ich verstehe... nur leider wollte ich ja nicht unbedingt mit einem Framework arbeiten.
Ich hab mir mal das hier durchgelesen
http://www.der-webdesigner.net/tutorials/php/325-php-und-oop-frontcontroller-und-templateengine

und was ich hier nicht genau nachvollziehen kann, ist der Umgang mit dem HttpRequest-Obj.
im nachfolgendem Aufruf und

Codeschnipsel aus dem Link:
PHP:
1 <?php
2
3     $rsv = new MyCommandResolver('path', 'defaultCommand');
4     $ctrl = new FrontController($resolver);
5     $req = new HttpRequest();
6     $res = new HttpResponse();
7     $ctrl->process($req, $res);

Wenn ich das so implementieren möchte, kommt ein Fehler, das das HttpRequest nicht gefinden wird.
also habe ich mal mit require_once('HTTP\request.php'); das Http_Request-Obj.
implementiert und aus "$req = new HttpRequest();" -> "$req = new \Http_Request();" gemacht dann kommt folgender Fehler:

Code:
Catchable fatal error: Argument 1 passed to Test\MVC\Controller\FrontController::run() must be an instance of Test\MVC\Controller\Request, instance of HTTP_Request given, called in C:\xampp\htdocs\Test\Test.php on line 11 and defined in C:\xampp\htdocs\Test\MVC\Controller\FrontController.php on line 6

Meine Implementierung für die ersten Gehversuche
PHP:
<?php
	namespace Test;
	
	use Test\MVC\Controller\FrontController;
	
	require_once('MVC\Controller\FrontController.php');
	require_once('HTTP\Request.php');

	$init = new FrontController;
	$req = new \Http_Request();
	$init->run($req);


Wie also benutze ich das HTTPRequest-Obj. richtig?
Ich arbeite mit XAMPP und habe nach langer suche herausgefunden, dass angebliche Klasse unter \xampp\php\pear\HTTP\request.php zu finden ist und durch require_once('HTTP\request.php');, dem Interpreter bekannt gemacht wird.
in manchen Tutorials wird immer davon ausgegangen, dass man schon alles weiß und lässt wesentliche Sachen - bei der Programmcode-Darstellung - weg. Und dann versteht man nur noch die Hälfte. Wie seht Ihr das?
 
Zuletzt bearbeitet:
Zurück