Webtechnologien Sommersemester 2019

Simple-MVC

Einleitung

Simple-MVC ist ein kleines PHP-Framework, das sich am Model-View-Controller-Muster orientiert und den Code von Websites grundlegend in drei Teile strukturiert.

Es bringt verschiedene Helfer mit, die sich beispielsweise um den Datenbankzugriff oder Session-Verwaltung kümmern. Zudem hat es einen Autoloader, der automatisch all jene Klassen lädt, die während des Programmablaufs benötigt werden.

Installation

Wir verwenden eine modifizierte Version – das Original finden Sie unter simplemvcframework.com oder auf GitHub. Achtung: Die originale Dokumentation kann teilweise abweichen.

  1. Laden Sie das Framework unter Simple-MVC.zip herunter.

  2. Entpacken Sie das Archiv.

  3. Verschieben Sie den neuen Ordner Simple-MVC in das Document-Root Ihres Webservers (htdocs, public_html o.ä.).

  4. Öffnen Sie die Datei config.php und ändern Sie die Konstante DIR auf die URL zum Verzeichnis (beispielsweise http://localhost/Simple-MVC/) Ändern Sie danach die Daten der Datenbank. Passen Sie auf, dass die Zeile define('DB_TYPE','mysql'); nicht auskommentiert oder gelöscht wird.

  5. Es wird eine Datei namens .htaccess mitgeliefert. Stellen Sie sicher, dass diese Datei vorhanden ist. (Unter Linux oder Mac OS ist sie unsichtbar – Sie können in der Shell per ls -a prüfen, ob sie vorhanden ist.)

Bevor wir anfangen, die Seite zu nutzen, noch eine Erklärung der Inhalte des Frameworks.

Verzeichnisse und Dateien

* Verzeichnis / Datei *         * Beschreibung *
├── config.php                  - Konfigurationsdatei
├── controllers                 - Alle Controller
│   └── products.php            - Beispiel für Produkte
├── core                        - Kerndateien von Simple-MVC
│   ├── bootstrap.php           - Herz des Ganzen
│   ├── controller.php          - Basis-Controller
│   ├── error.php               - Fehler-Controller
│   ├── logger.php              - Loggt Informationen
│   ├── model.php               - Basis-Model
│   └── view.php                - Basis-View
├── errorlog.html               - Fehler-Logbuch
├── helpers                     - Alle Helfer
│   ├── database.php            - Datenbankzugriff
│   ├── message.php             - Nachrichtenspeicher
│   ├── password.php            - Passwortgenerierung
│   ├── session.php             - Siztungsverwaltung
│   └── url.php                 - Adress-Helfer
├── index.php                   - Startpunkt
├── models                      - Alle Models
│   └── products_model.php      - Beispiel für Produkte
├── static                      - Unveränderliche Dateien
│   ├── css                     - Stylesheets
│   │   ├── bootstrap.min.css   - Bootstrap
│   │   └── style.css           - Eigene Styles
│   └── img                     - Platzhalter für Bilder
└── views                       - Alle Views
    ├── error                   - Verschiedene Fehler
    │   └── 404.php             - Datei nicht gefunden
    │   └── default.php         - Standardfehler
    ├── footer.php              - Fußbereich der Seiten
    ├── header.php              - Kopfbereich der Seiten
    ├── message.php             - Nachrichten
    └── products                - Produkte
        ├── form.php            - Formular
        └── list.php            - Auflistung

Model View Controller

Stellen wir uns den Ablauf des Programms als Bestellung bei einer Lieferapotheke vor. Das Geschäft hat drei Mitarbeiter: Den Apotheker oder Manager, einen Lagerarbeiter und einen Boten.

Der Manager nimmt die Bestellungen der Kunden entgegen und prüft sie, beispielsweise auf Sinnhaftigkeit (Gibt es das Produkt überhaupt?) und Legalität (Darf der Kunde das Produkt z.B. rezeptfrei haben?). Nach erfolgreicher Prüfung bittet er den Lagerarbeiter, ihm alle nötigen Produkte zu bringen. Der Manager selber kennt sich im Lager nicht aus – muss er auch nicht, denn die Zuständigkeiten sind klar getrennt und jeder auf seinen Bereich spezialisiert. Erhält der Manager die gewünschten Produkte, übergibt er sie dem Boten, der sie fachgerecht verpackt und dem Kunden bringt.

Im MVC-Framework nimmt die Rolle des Managers der Controller ein, die des Lagerarbeiters das Model und die des Boten die View.

Routing

Bevor wir uns die Aufgabenverteilung genauer ansehen, noch ein paar Worte zu den benutzten URLs.

Wie die meisten Frameworks ermöglicht Simple-MVC sogenannte “hübsche URLs”, die meist ohne Dateiendungen und Query-Parameter auskommen.

# nicht so hübsch
http://beispi.el/index.php?category=users&id=alice&site=photos
# hübsch
http://beispi.el/users/alice/photos

Um intern trotzdem nur einen Einstiegspunkt zu haben, wird der Webserver angewiesen, die URL vor der weiteren Verarbeitung umzuschreiben. Dazu bietet der Apache Webserver eine Rewrite-Engine in Form des Moduls mod_rewrite, das über die Datei .htaccess benutzt werden kann. Das sieht bei Simple-MVC so aus:

RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]

Hierbei wird jede URL so verändert, dass sie als Wert des Parameters url an die index.php übergeben wird.

Simple-MVC benutzt eine spezielle URL-Struktur, die folgendem Muster entspricht:

http://Host/Controller/Funktion/Parameter

Jede Anfrage wird, wie oben beschrieben, an an die index.php umgelenkt, die ein neues Objekt der Klasse Bootstrap erzeugt. Dieses entscheidet dann, welcher Controller für die weitere Verarbeitung der Anfrage verantwortlich sein soll. Ist kein entsprechendes Fragment in der URL enthalten, wird der Default-Controller benutzt, der in der index.php definiert ist. Zudem wird anhand der URL entschieden, welche Funktion des Controllers aufgerufen werden soll und welche Parameter zu übergeben sind. Ist kein entsprechendes Fragment enthalten, wird die Funktion index aufgerufen.

// http://localhost/Simple-MVC/
$controller = new $defaultController();
$controller->index();

// http://localhost/Simple-MVC/users/delete/bob
$controller = new Users();
$controller->delete('bob');

Controller

Der Controller enthält die Programm-Logik, also jene Teile, in denen entschieden wird, was unter welchen Bedingungen zu tun ist. Von hier aus werden dem Model Daten übergeben oder von jenem empfangen. Und letztlich werden die fertigen Daten an die View übergeben, gerendert und an den Besucher der Seite übermittelt.

Controller erben immer von – Überraschung – der Klasse Controller, oder einem seiner Kinder. Und sie verfügen standardmäßig über die Variablen _model und _view, über die auf das dazugehörige Model und eine allgemeine View zugegriffen werden kann. Beispiel:

$this->_model->get($id);
$this->_view->render('header', $data);

An dieser Stelle ist es noch einmal wichtig, sich die HTTP-Methoden ins Gedächtnis zu rufen. Da Links immer über GET funktionieren, müssen wir Methoden wie DELETE (siehe obiges Beispiel: …/users/delete/bob) als Teil der URL nachbauen. Das ist insofern problematisch, als dass wir zwei Wünsche in einer Anfrage haben: Löschen eines Nutzers und Anzeigen einer Seite. Um diesen Konflikt zu lösen, werden die zwei Teile getrennt: Um ein Produkt zu löschen, wird die entsprechende URL angesteuert, aber keine View gerendert, sondern direkt danach auf eine andere Seite weitergeleitet – beispielsweise eine Übersicht aller Nutzer. Dadurch wird auch verhindert, dass z.B. ein Lösch-Befehl versehentlich mehrfach angefragt, geteilt oder gebookmarkt (o.O) wird.

Beispiel-Ablauf des Lösch-Prozesses

Model

Das Model kümmert sich um alles, was mit Daten zu tun hat und schreibt in die Datenbank oder liest von dort. Zu unserem User-Controller gäbe es ein Model User_Model, das einzelne oder mehrere User ausgeben, neue anlegen sowie vorhandene ändern und löschen kann. Models müssen im Ordner models liegen, wie der Controller heißen und das Suffix _model tragen. (Heißt die Datei des Controllers user.php, muss die des Models user_model.php heißen.)

View

Über die View werden unsere Templates aus dem Ordner view gerendert. Ein Template enthält HTML- und PHP-Code zur Ausgabe und wenig bis keine eigene Logik. Versuchen Sie Templates zu bauen, die eine – und nur eine – Aufgabe erfüllen. Und kombinieren Sie diese Templates später zu größeren. Haben Sie beispielsweise ein Suchformular, sollten Sie es in eine eigene Datei auslagern und diese in andere Templates einbinden.

Das Template kann nur Daten rendern, die ihm auch übergeben wurden. In Simple-MVC wird dafür das Array $data als Container benutzt. Diesem können im Controller jederzeit neue Schlüssel und Werte zugeordnet werden. Im Beispiel finden Sie den Schlüssel title, der später über die Funktion render an das Template header überreicht und dort ausgegeben wird.

$data['title'] = 'Übersicht';
   ...
$this->_view->render('header', $data);
   ...
<title><?= $data['title'] ?></title>

Helfer

Simple-MVC bringt verschiedene Helfer mit, die Sie bei wiederkehrenden Aufgaben unterstützten.

Database

Hier sind die üblichen Datenbankoperation mit PDO implementiert. Dadurch müssen in den Models nicht mehr alle SQL-Statements geschrieben werden, sondern es reicht der passende Aufruf einer Funktion in Database. Models haben immer eine Referenz auf ein Datenbank-Objekt namens _db. Beispiel zum Anlegen eines neuen Nutzers:

$this->_db->insert('users', $data);

Message

Sollen einem Nutzer zu einem späteren Zeitpunkt Nachrichten angezeigt werden (beispielsweise über das erfolgreiche Anlegen oder Löschen eines Produkts), können die zwei Funktionen von Message benutzt werden. Über Message::set('nachricht') wird für einen Benutzer eine Nachricht gesetzt und solange gespeichert, bis sie über Message::show() ausgegeben wird.

Password

Dient zur Handhabung von Passwörtern und bietet zwei Funktionen: hash zum Erzeugen eines kryptographischen Hashs per PBKDF2 und validate zum Vergleichen eines gegebenen Passworts mit dem korrekten Hash. Mehr zu dem Thema finden Sie unter Authentifizierung mit PHP.

Session

Hierüber kann einem Benutzer eine eindeutige Session-ID gegeben werden, die über einen Cookie gespeichert und bei jeder Anfrage mit übertragen wird. Dadurch lässt sich eine Anfrage eindeutig einem Benutzer zuordnen (was durch das zustandslose HTTP nicht vorgesehen ist) und beispielsweise Features wie das Einloggen von Benutzern umsetzen. Der beschriebene Helfer Message setzt auch auf Session.

URL

Hierbei handelt es sich um einen sehr kleinen Helfer, der es ermöglicht, Benutzer über einen HTTP-Redirect (standardmäßig Status-Code 303) zu einer anderen Stelle der Seite umzuleiten. Zudem gibt die Funktion STYLES('name') den absoluten Pfad einer CSS-Datei zurück, um sie leichter in den Templates einbinden zu können.