In meiner Zeit der Umschulung habe ich einiges gelernt. Doch was ich bei vielen gemerkt habe, ist das die großen Aufgaben in der Webentwicklung oft waren, zu verstehen wie man mit MVP oder MVVP arbeitet. Um das ganze ein wenig verständlicher für Menschen zu machen, die nun eine Umschulung machen oder aus der Umschulung raus sind und das noch mal vertiefen wollen, möchte ich hier eine Erklärung darstellen, die helfen soll das Konzept zu verstehen und in sich zu verinnerlichen. Ich versuche es grundlegend so verständlich wie mir möglich zu schreiben. Bei Fragen stehe ich aber gerne bereit.
Die Grundstruktur verstehen
MVP bedeutet ausgeschrieben Model-View-Presenter also auf Deutsch Modell-Ansicht-Präsentator und wird grundlegend bei Desktopanwendungen genutzt, findet aber auch in der Webentwicklung seine Anwendung. Viele setzen heutzutage auf Javascript Frameworks, was dazu führt, das man sich direkt tief in die Elemente der Programmierung und des Routing wirft. Ohne die Elemente zu verstehen und im Kopf gerne mal ins Chaos zu geraten, gerade wenn es dann um Routing geht. MVP dient aber grundlegend eigentlich der besseren Wartbarkeit einer Anwendung und ist in der Basis leicht zu verstehen. Wir brauchen eine Struktur die uns eine Übersicht schafft. Hierzu bauen wir das ganze wie folgt auf:
Shared Hosting Aufbau von MVP

In der MVP für normalen „Shared Hosting“ haben wir den Aufbau innerhalb unseres Webverzeichniss direkt. In dieser Methode benötigen wir eine zusätzliche Absicherung durch eine .htaccess. Im Fall dieser Ansicht kommt in den „assets“ Ordner alles was ein Design oder Wirkwert hat. Also CSS (Cascading Style Sheets) Dateien, aber auch JS(Javascript) Dateien.
Unser Bereich „src“ ist der Bereich der nun alle wichtigen Logiken und Ansichten verarbeitet. Also eben unsere MVP Struktur. Sie beinhaltet:
- Controler = Verwaltet unsere Router zu den Views und Models
- Model =PHP Klassen die Logik verarbeiten wie zum Beispiel die Datenbank Klasse
- View = Dateien die der Darstellung der Seite dienen
.htaccess bestimmt die Regelungen. In diesem Fall, das wir nicht zulassen das src direkt im Browser aufgerufen werden kann. Unsere index.php dient als Zentraler Verwalter für alles.
Aufbau bei Vollzugriff Hosting oder Servern

In der MVP außerhalb von SharedHosting haben wir den Aufbau außerhalb von unserem Webverzeichniss und haben im Webverzeichniss selber „public“ unsere oben genannten „assets“ und .htaccess wie index.php zugänglich . Die Beschreibungen bleiben die gleichen wie oben genannt, nur die Struktur verändert sich ein wenig.
- public ist das für User zugängliche Webverzeichnis
- src liegt außerhalb der öffentlich zugänglichen Zone und kann von Haus aus nicht betreten werden.
- Der Composer ist optional, dient hier aber als Verwaltung der PHP Erweiterungen zum Autoloading der Klassen und die Verwaltung externer Bibliotheken
Index.php Aufbau verstehen bevor es los geht
Die Index Datei brauch ein wenig Vorbereitungsarbeit. Und daher ich weiß wie gerne man Copy&Paste betreibt, rate ich ganz klar dazu es durchzulesen und nicht einfach zu kopieren. Verstehen kannst du es nur, wenn du es dir durch diese Hilfe selber erklärst.
Vorbereitung mit Composer
Da wir sauber arbeiten wollen, nutzen wir auch in der Shared Hosting Version den Composer für PHP. Dazu laden wir diesen herunter und installieren diesen. Ich selber arbeite mit VSCode sehr gerne und öffne dementsprechend in meinem Projekt nun einen Terminal. Hier fragt die der Composer beim Befehl:
composer init
Nach Eingaben die zu tätigen sind. Ich gehe davon aus das du die Dokumentation lesen kannst und selber diese kleine Arbeit einrichten kannst. In der Paketsuche nehmen wir dann das Paket
Enter package # to add, or the complete package name if it is not listed:
rooc/psr4_auto_loader
Enter the version constraint to require (or leave blank to use the latest version):
Using version ^2.0 for rooc/psr4_auto_loader
Jetzt haben wir das wichtigste Paket schon mal drinne. Folge den Schritten weiter. Wenn es fertig ist, legen wir mit der nächsten Arbeit los
Index Datei beginnen
Wir beginnen in der Index mit der Bestimmung vom Bereich „KONFIGURATION & BOOTSTRAPPING„, sie dient den wichtigen Vorbereitungen. Ich kommentiere innerhalb vom Code zum verständnis was ich tue und erkläre danach welchen Sinn es hat.
<?php
// =================================================================
// 1. KONFIGURATION & BOOTSTRAPPING
// =================================================================
// Session starten, unerlässlich für Logins, Flash-Nachrichten etc.
session_start();
// Fehler-Reporting für die Entwicklungsumgebung einschalten.
error_reporting(E_ALL);
ini_set('display_errors', '1');
// Wichtige Pfad-Konstanten definieren, um die Struktur flexibel zu halten.
define('ROOT_DIR', __DIR__);
define('SRC_DIR', ROOT_DIR . '/src/');
define('VIEWS_DIR', SRC_DIR . 'View/'); // Korrigiert: Der Views-Ordner ist in srcUnsere erste Arbeit in der Index Datei ist also die Setzung von einer session mit dem „session_start();“ und damit wir jederzeit alle Fehler in der Entwicklung sehen machen wie ein komplettes ErrorReporting an, wichtig ist hier, dieses auszuschalten, wenn wir es in die Öffentlichkeit geben also Produktivumgebung später erzeugen.
Um sicher zu gehen, das alle Pfade erkannt werden trotz Autoloader, setzen wir die Pfad-Konstanten. Hier bestimmen wir bei „ROOT_DIR“ das Rootverszeichniss von unserem Projekt, bei „SRC_DIR“ den Platz von unserem src Ordner und danach unseren „VIEWS_DIR“ für die View Dateien. Zu beachten ist hier die Besonderheit vom Aufbau, der oft nicht beachtet wird und eigentlich schon ein wichtigen Punkt erklärt. Und zwar
// Unser Root Verzeichniss
define('ROOT_DIR', __DIR__);
// Im 2 Bereich also nach 'SRC_DIR', rufen wir erst ROOT_DIR auf und dann geben wir den Ordner an
define('SRC_DIR', ROOT_DIR . '/src/');Wir setzen also innerhalb der Deffinition die Ansage das wir „SRC_DIR“ bestimmen wollen und geben dann an, das der Pfad von unserem src Ordner im ROOT Verzeichniss sitzt und dann geben wir den eigentlichen Pfad danach an durch ‚/src/‚. Das ist eine leichte Bestimmung die absichert das immer vom Hauptverzeichniss aus alles zu finden ist.
Autoloader Ansatz bestimmen
Nun müssen wir unseren Autoloader in unsere Datei einbinden. Also machen wir 2 Zeilen tiefer nun den Bereich „Autoloader“:
/**
* Lädt Klassen automatisch, wenn sie benötigt werden.
*/
spl_autoload_register(function ($className) {
// Ersetzt den Namespace-Separator '\' durch den Verzeichnis-Separator '/'
$file = SRC_DIR . str_replace('\\', '/', $className) . '.php';
if (file_exists($file)) {
require_once $file;
}
});wir rufen nun also die Funktion „spl_autoload_register“ auf die wir aus unserer Autoloader Erweiterung holen. Diese bekommt den Inhalt „function ($className)“ also sagen wir der Funktion direkt das sie eine Funktion ist mit dem jeweiligen KlassenNamen der in der Variable „$className“ geladen wird.
Wichtig ist hier zu merken! Die Funktion erwartet eine Namespace-Struktur, die der Ordnerstruktur in /src entspricht. Das heißt für uns in unserem Beispiel, das sie nun weiß, die Klasse \Controler\HomeController wird in /src/Controler/HomeController.php gesucht.
Ansonsten gibt es hier nicht viel zu erklären, daher es mit sehr normalen Möglichkeiten arbeitet. Sollten doch noch Fragen sein, könnt Ihr diese natürlich stellen.
ROUTING bestimmen (jetzt wird es knackig)
Kommen wir zu dem Thema, was manche nicht gut verstehen oder auch manchmal nicht gut erklärt bekommen. Routing tut manchen wirklich im Kopf weh, dabei ist es eigentlich kein Hexenwerk. Wir kommen nun zur VerkehrsPolizei unserer Webseite:
// Die angefragte URL auslesen und bereinigen.
// Fallback auf '/', falls REQUEST_URI nicht gesetzt ist (z.B. bei CLI-Ausführung).
$requestUri = trim($_SERVER['REQUEST_URI'] ?? '/', '/');
// Die URL in ihre Bestandteile zerlegen: /controller/method/param1/param2
$parts = explode('/', $requestUri);
// Controller, Methode und Parameter extrahieren.
// Standardwerte: HomeController, index-Methode, keine Parameter.
$controllerName = !empty($parts[0]) ? ucfirst($parts[0]) . 'Controler' : 'HomeController';
$methodName = $parts[1] ?? 'index';
$params = array_slice($parts, 2);Stelle dir vor, die ganze Webseite ist eine große Stadt, und jedes Mal, wenn jemand eine Seite aufruft (eine Adresse eingibt), ist das ein Auto, das in die Stadt fährt. Dieser Code-Abschnitt ist der Verkehrspolizist an der Hauptkreuzung: Er liest die Adresse (URL) und schickt das Auto genau an das richtige Ziel, damit der richtige Inhalt angezeigt wird.
Bei „$requestUri = trim($_SERVER['REQUEST_URI'] ?? '/', '/');“ wird die Adresse ausgelesen und bereinigt. Das heißt erstmal liest der Code die Adresse:
Heißt! Wenn jemand meine-seite.de/dashboard/ eingibt, liest der Code den Teil nach dem .de – also /dashboard/ . Das „trim“ entfernt unnötige Schrägstriche (/) am Anfang und Ende. Aus „/dashboard/“ wird so nur noch „dashboard„.
Wenn jemand nur die Startseite (meine-seite.de) aufruft, sorgt der Code dafür, dass trotzdem ein / (Schrägstrich) übrig bleibt, damit die Website nicht abstürzt. Das ist die Startposition.
Adresse in Einzelteile legen
Bei „$parts = explode('/', $requestUri);“ nimmt der Code die saubere Adresse (Beispiel: blog/beitrag-1) und zerlegt sie an jedem Schrägstrich (/) wie ein Puzzle. In der Liste „$parts“ stehen nun bei dem Beispiel 2 Listeneintrage:
- blog
- beitrag-1
Nun wird noch die Rolle bestimmt
Jetzt werden die Teile des Puzzles den Rollen zugeordnet, damit der Computer weiß, was er tun soll:
$controllerName = !empty($parts[0]) ? ucfirst($parts[0]) . 'Controler' : 'HomeController';
$methodName = $parts[1] ?? 'index';
$params = array_slice($parts, 2);Um das zu erklären machen wir mal eine Tabellenübersicht. Dieser Code macht wirklich wichtige Arbeit.
| Teil der URL (Adresse) | Code-Name | Was bedeutet es für die Webseite? | Beispiel (home/show/15) |
| Erster Teil | $controllerName | Das ist die Haupt-Zuständigkeit (z.B. die „Blog-Abteilung“ oder die „Hauptseiten-Abteilung“). Der Code hängt immer das Wort „Controller“ an. | Der Wert wird zu HomeController. |
| Zweiter Teil | $methodName | Das ist die spezifische Aufgabe innerhalb der Abteilung. | Der Wert wird zu show (zeigen). |
| Dritter Teil und weitere | $params | Das sind Zusatz-Informationen, die für die Aufgabe wichtig sind (z.B. welche ID die Seite hat). | Der Wert wird zu 15. |
Das Ergebnis der Routing Arbeit ist also: Wenn Sie /home/show/15 eingeben, weiß der Code: „Ich muss den HomeController aufrufen, dort die Aufgabe show ausführen und ihr die Zahl 15 übergeben, um die Seite Nummer 15 anzuzeigen!“
Und wenn nichts in der Adresse steht (nur die Startseite), wählt der Code automatisch den HomeController mit der Aufgabe index (die Startseite anzeigen).
Das ist also die Arbeit von unserem Routing. Nachdem wir das nun besprochen haben, vertieft dieses Thema für euch. Gerne könnt ihr hierzu auch mal den Beitrag von „Dave Hollingworth“ als Video ansehen, dieser geht noch mal tiefer in die Router Materie ein.
Nun gehen wir zum DISPATCHER
Der Dispatcher kümmert sich um die Weiterleitung an den Controller im Ordner Controler. Unser Codebereich in der Index sieht also wie folgt aus:
// Den vollständigen Klassennamen des Controllers zusammenbauen.
$controllerClass = 'Controler\\' . $controllerName;
if (class_exists($controllerClass)) {
$controllerInstance = new $controllerClass();
if (method_exists($controllerInstance, $methodName)) {
// Die Methode aufrufen und die URL-Parameter übergeben.
call_user_func_array([$controllerInstance, $methodName], $params);
} else {
// Methode nicht gefunden
sendNotFound("Methode '$methodName' im Controller '$controllerName' nicht gefunden.");
}
} else {
// Controller-Klasse nicht gefunden
sendNotFound("Controller '$controllerName' nicht gefunden.");
}Sieht unübersichtlich aus? Eigentlich nicht. Aber es kann einen schon mal ein wenig überlaufen wenn man neu im Bereich Coding ist oder zumindest neu im vertieften Programmieren von PHP. Was hier passiert ist aber einfach ein gewohntes if und else disaster ^^. Warum Disaster? Weil eine if und else in einer if und else ist. Das macht manche ein wenig durcheinander in der Übersichtlichkeit, aber auch hier haben wir kein Hexenwerk, wenn wir es logisch anschauen. Zerlegen wir den Code einfach mal.
if (class_exists($controllerClass)) {
$controllerInstance = new $controllerClass();Der if Bereich kontrolliert ob die Klasse existiert die wir übergeben. Also die Klasse welche mit „$controllerClass“ übergeben wird. Wenn das so zutreffend ist arbeitet dieser if Bereich weiter und fängt an eine Instanz zu eröffnen durch „$controllerInstance = new $controllerClass();„.
Danach beginnt der nächste innerliche if Bereich:
if (method_exists($controllerInstance, $methodName)) {
// Die Methode aufrufen und die URL-Parameter übergeben.
call_user_func_array([$controllerInstance, $methodName], $params);Er prüft nun ob die Methode innerhalb der Controller Instanz vorhanden ist. Und wenn diese vorhanden ist, dann ruft er die Methode auf. Wenn es nicht zutreffend ist, geht er in die Else über:
} else {
// Methode nicht gefunden
sendNotFound("Methode '$methodName' im Controller '$controllerName' nicht gefunden.");
}Wenn vorher sogar schon bei der Prüfung der vorhandenen Klasse ein False entsteht, geht es in den ersten else Bereich, wo folgendes passiert:
} else {
// Controller-Klasse nicht gefunden
sendNotFound("Controller '$controllerName' nicht gefunden.");
}Wenn es also nicht zutreffend ist das die Klasse existiert, dann geht er in diesen Else Bereich und sendet dort eine Nachricht in unser Webfenster, das der Controller mit den Namen „$controllerName“ aus dem Ordner Controler nicht gefunden werden kann.
Verständlich? Eigentlich kein wirkliches Chaos, sondern ein wirklich gut überlegter sauberer Vorgang der einfach eine kleine tiefere Verschachtelung hat.
HELPER-FUNKTIONEN
Wir sind im letzten Bereich von unserer Index Datei und zwar im Bereich der Helper Funktionen. Hier kommt der kleine Polizist dem Dorfpolizist zur Hilfe um den Verkehr und dessen Masse zu verarbeiten. In diesem Fall halten wir diesen Bereich aber sehr einfach, indem wir nur eine Funktion dafür schreiben und zwar eine Funktion die Informationen gibt, wenn keine Seite gefunden wurde.
/**
* Sendet eine 404 Not Found Antwort und beendet das Skript.
* @param string $message Die anzuzeigende Fehlermeldung.
*/
function sendNotFound(string $message = 'Seite nicht gefunden.'): void
{
header("HTTP/1.0 404 Not Found");
require_once VIEWS_DIR . 'errors/404.php';
echo "<h1>Fehler 404</h1>";
echo "<p>$message</p>";
exit;
}Ich denke eine Erklärung ist hier nicht wirklich nötig. Daher es sich aus den anderen Bereichen ergibt und die Arbeit in diesem Bereich durch leichten Code erfüllt wird.


Schreibe einen Kommentar