Model-View-Controller in PHP
Das wohl häufigste Design Pattern in PHP ist das Model-View-Controller Entwurfsmuster (MVC). Dieses wird ebenfalls in diversen PHP Frameworks wie beispielsweise Laravel umgesetzt und sollte jedem professionellem PHP-Entwickler bekannt sein.
Beim MVC-Muster wird euer Klassendesign in drei verschiedene Kategorien unterteilt:
- Model: Für jede "Entität" in eurer Datenbankstruktur besitzt ihr eine eigene Model-Klassen, beispielsweise für einen Online-Shop eine Model-Klasse für User, eine Model-Klasse für die Artikel, eine Model-Klasse für Kommentare, ein Model-Klasse für Bestellungen usw. Eure Model-Klassen sind dabei eng gekoppelt an eure Datenbankstruktur und sorgen dafür, dass Daten aus der Datenbank geladen und gespeichert werden.
- View: Die View ist für die Darstellung eurer Daten verantwortlich. Dies sind beispielsweise Templates um eure Website mittels HTML und CSS auszugeben. Wichtig im View ist, dass diese keine Logik implementiert, d.h. es erfolgen keine Datenbankabfragen und keine Überprüfung von Werten. Die View kümmert sich bloß um die Darstellung eurer Anwendung.
- Controller: Die Controller-Klassen sorgen dafür, dass eure Datenschicht (euer Model) mit eurer Präsentationsschicht (eurem View) verheiratet wird. In diesen Klassen implementiert ihr die Logik eurer Anwendung. Beispielsweise bekommt der Controller vom View die Login-Daten, diese überprüft der Controller ob der Benutzer existiert und ob das Passwort korrekt ist. Falls alle Eingaben korrekt waren, veranlasst der Controller die Darstellung der nächsten Seite.
Inhaltsverzeichnis
Warum MVC?
Der große Vorteil von Model-View-Controller (MVC) ist die klare Trennung zwischen der Verarbeitung von Daten in eurem Model (z.B. Erstellen, Suchen und Löschen von Nutzern), eurer Darstellung der Daten im View (typischerweise HTML Seiten) und der Anwendungslogik im Controller (z.B. Abfragen von Benutzereingaben vom View und das Senden dieser ans Model).
Müsst ihr beispielsweise etwas an eurer Datenbankstruktur ändern, z.B. der Spaltenname der E-Mail-Adresse des Users wird umbenannt, so müsst ihr diese Änderung nur in der Model-Klasse für den User ändern. Eure Anwendungslogik (Controller) und Darstellungsschicht (View) bleibt davon unberührt. Möchtet ihr dagegen neben einer Web-Version eurer Anwendung diese erweitern um eine Mobile-Version, so müsst ihr nur eure Templates im View anpassen.
Diese Trennung zwischen den Schichten macht es auch deutlich leichter mit mehreren Personen an der selben Webanwendung zu arbeiten. Euer Webdesigner muss beispielsweise nur die Templates im View modifizieren und kommt mir dem übrigen Programmcode eurer Anwendung nicht in Berührung. So können selbst Personen die kein Verständnis von PHP haben recht einfach mit ein paar HTML-Kenntnissen eure Darstellung der Anwendung anpassen.
Das Model
Im Model programmiert ihr den Zugriff auf eure Datenbanktabellen. Meistens besitzt ihr pro Datenbanktabelle eine entsprechende Model-Klasse, die Anfragen auf diese Datenbanktabelle steuert. Nachfolgend ein Beispiel wie so eine Klasse für eure User-Klasse aussehen könnte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php //$pdo enthält die Datenbankverbindung class User { // Klasse zur Abbildung eurer Benutzer public static function newUser($email, $vorname, $nachname, $passwort) { } public static function findByEmail($email) { } public function delete() { } public function setPassword($newPassword) { } } class Product { // Klasse zur Darstellung von Produkten im Online-Shop ähnlich wie oben //... } class ProductOrder { // Klasse zur Darstellung neuer Produktbestellungen public function __constructor(User $user) { } public function addProduct(Product $product) { } public function addProductById($productId) { } public function buy() { } } ?> |
Durch das Model wird eure weiterfolgende Programmierung deutlich vereinfacht. Nachfolgend ein Beispiel:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php $order = new ProductOrder($max); $order->addProductById(23); $order->addProductById(42); if($order->buy()) { echo "Gesamtpreis der Bestellung: ".$order->price(); } ?> |
Die View
In der View definiert ihr, wie eure Daten dargestellt werden sollen. Typischerweise bedeutet dies, dass ihr gewisse HTML Templates besitzt in der eure Daten reingeladen werden. Viele PHP-Frameworks bieten bereits eine Template-Engine an, mit der ihr die Darstellung der Daten trennen könnt von der Logik für die Datenverarbeitung. Solltet ihr auf kein MVC-Framework wie Laravel zurückgreifen, so empfiehlt sich die Nutzung existenter Template-Engines, beispielsweise Smarty.
Die einfachste Template-Engine ist die Verwendung der include-Funktion:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php $order = new ProductOrder($max); $order->addProductById(23); $order->addProductById(42); if($order->buy()) { include("order-successful.view.php"); } else { include("order-failed.view.php"); } ?> |
order-successful.view.php
1 |
Deine Bestellung über <?php echo $order->getPrice(); ?> war <b>erfolgreich</b>. |
order-failed.view.php
1 |
Deine Bestellung ist leider <b>fehlgeschlagen</b>. Bitte kontaktiere unseren Support. |
Der Controller
Der Controller implementiert eure Anwendungslogik. Möchte sich ein neuer Benutzer registrieren, so müssen die entsprechenden Daten aus dem Formular abgefragt werden, ein neuer Datenbankeintrag muss erstellt werden, ggf. muss eine Willkommensemail gesendet werden, und dem Benutzer muss angezeigt werden, dass seine Registrierung erfolgreich war.
Sofern man auf kein MVC-Framework setzt wie beispielsweise Laravel, dann kann die Implementierung des Controllers etwas umständlich werden. Ich persönlich erstelle dazu gerne eine weitere Klasse, z.B. die UserController. Diese beinhaltet verschiedene Logik nützlich für eure Anwendung.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php class UserController { public function registerNewUser($email, $password) { //Entsprechende Überprüfungen und SQL Queries zum Registrieren des Nutzers //Gibt z.B. true zurück, falls die Registrierung funktioniert hat } public function changeUserPassword(User $user, $new_password) { //Ändert das Benutzerpasswort für den Nutzer $user } public function sendNewPassword(User $user) { //Sendet dem Benutzer ein neues Passwort zu } } ?> |
Eure Scripts für die verschiedenen Bereiche können dann auf diesen Controller zurückgreifen.
Euer Registrierungsscript z.B.:
1 2 3 4 5 6 7 8 9 |
<?php $userController = new UserController(); if($userController->registerNewUser($_POST['email'], $_POST['password'])) { include("templates/registerSuccess.inc.php"); } else { include("templates/registerFailed.inc.php"); } ?> |
Abschlussbemerkungen
Die Verwendung von Models und Views ist für jede größere Anwendung nützlich und sinnvoll. Die Implementierung einer Controller-Klasse kann etwas komplizierter sein, sofern man auf kein MVC-Framework zurückgreift. Dort erstelle ich meistens Klassen um häufig genutzte Funktionalität zusammen zu fassen und den Rest der Anwendungslogik wird in gewöhnliche PHP Dateien ausgelagert.
Bei größeren Projekten (ab 5.000 Zeilen Programmcode) kann ich sehr den Einsatz eines MVC-Frameworks wie Laravel empfehlen.
Autor: Nils Reimers