Es wächst und gedeiht: Einstieg in Model-View-Controller

Für das Winzen-Projekt, das ich mit den Forschungsdaten von Christian Günther als eine Art öffentliches Tutorial für interessierte „Digital Humanities“-Anhänger baue, hatte ich ja CodeIgniter als Framework ausgewählt. Ursprünglich wollte ich ja gerne alles transparent von Grund auf programmieren, aber das wäre echt etwas zu heftig geworden, um es einigermaßen zeitnah neben der Arbeit zu machen. Inzwischen ist das erste Teilziel für das Winzen-Projekt erreicht und unter winzen.danielstange.de mit den elementarsten Funktionen auch nutzbar.

Ein Framework bietet den Vorteil, dass typische Aufgaben, die jede Webanwendung benötigt, bereits vorgefertigt existieren. Das ermöglicht in der Regel, dass der allermeiste Programmcode, den man schreiben muss, tatsächlich projektrelevant ist. Für CodeIgniter habe ich mich eher aus Zufall entschieden – es ist bei OpenShift vorinstalliert verfügbar, hinreichend einfach erlernbar und trotzdem so komplett, dass man eigentlich rein projektspezifisch programmieren kann. Der komplette Quellcode zum Projekt liegt wie immer auf BitBucket zum Herunterladen.

Und deshalb will ich direkt einsteigen, zwei elementar wichtige Konzepte für das Programmieren heutzutage an Code-Beispielen aus dem Winzen-Projekt zu zeigen.

Nummer eins ist seit Jahrzehnten Standard: Die Segmentierung von Programmen in ihre namensgebenden Aufgabenbereiche: Model – View – Controller, kurz MVC. Übergelagert ist die Idee der „Separation of Concerns“, und beim MVC-Paradigma sind die „Concerns“ (sinngemäß etwa „Zuständigkeiten“ oder „Anliegen“) sind die folgenden:

Model – Das Datenmodell der Anwendung. Das Model repräsentiert die Schicht, die die Bereithaltung der Daten regelt. Speichermedium und -form gehört hier streng genommen nicht dazu – das Model hält gewissermaßen einen Anschluss bereit, wo man einen Speicher „andockt“, es liest die Daten, speichert sie nach Veränderungen usw. Der Charme hierbei ist: Wenn man die Daten zum Speichern in einer Datenbank normalisiert, sind sie auf viele Tabellen verteilt. Das Model baut daraus wieder verschiedene Formen zusammen. Denkt man z.B. an die Personen der Winzengruppe, dann möchte man eine Liste aller Mitglieder zum Überblick, eine Detailseite pro Person etc. Das übernimmt das Model „Person“, das (nicht überraschend) eine Repräsentation der Tabelle „Person“ in der Datenbank ist. Man kann es in der Anwendung auf zwei Wegen ansprechen – einmal ohne Parameter, dann liefert es eine Liste von Personen. Einmal mit Parameter, dann liefert es die Daten einer bestimmten Person. So sieht das Person-Model im Code aus: (kompletter Quellcode hier)

class Person_model extends CI_Model
{
  public function __construct() {
    $this->load->database();
  }

  public function get_person($pid = 0){
    
    if ($pid === 0)
    {
      $query = $this->db->get('Person');
      return $query->result_array();
    }

    $query = $this->db->get_where('Person', array('PersonId' => $pid));
    return $query->row_array();
  }
}
  • Zeile für Zeile
  • (1) Eine neue Klasse („Bauplan“) namens Person_model wird erzeugt, das die bestehende Klasse CI_Model erweitert (dazu später mehr).
  • (3) Der sogenannte Konstruktor: __construct ist in PHP eine magische Methode, die immer ausgeführt wird, wenn das Personen-Modell instanziiert wird.
    (4) Der Konstruktor sendet dem Load-Objekt des aktuellen Kontexts die Aufforderung, die Datenbank bereitzustellen. Bedeutet: Im aktuellen Kontext („$this“) wird das Objekt „db“ bereitgestellt,
  • (7) get_person ist die eigentliche Methode des Models. Sie empfängt einen Parameter (Argument) und wenn dieser nicht existiert, wird er standardmäßig auf 0 gesetzt. Der Modifier „public“ besagt: Die Methode ist für alle anderen Code-Teile ansprechbar
  • (8) es wird geprüft: ist $pid (das Argument der Methode) exakt 0
  • (10) ist das so, wird $query mit dem Rückgabewert der Methode „get(‚Person‘) des eben erzeugten Datenbankobjekts befüllt. Konkret: Hier wird die Treffermenge „hole alle Datensätze aus der Tabelle ‚Person'“ ausgeführt.
  • (11) Die Methode gibt etwas zurück (durch „return“), und zwar eine Liste (Array) der Datensätze, die auf die Abfrage in (10) zurückgeliefert werden.
  • (13) Wenn $pid nicht exakt 0 ist, wird eine andere Methode von db ausgeführt, nämlich get_where(). Die empfängt die Argumente ‚Person‘ (den Tabellennamen) und ein Array (eine Liste) aus ‚PersonId‘, einem Zuweisungsoperator => und $pid. Das sind die Argumente der WHERE-Klausel: Spaltenname und -wert als sogenanntes Key-Value-Paar.
  • (14) die Query (Abfrage) wird ausgeführt und die Treffermenge zurückgeliefert. Statt „result_array()“ wird jetzt die Methode „row_array()“ aufgerufen. Da exakt ein oder kein Datensatz herauskommen kann, brauchen wir hier keine Umstände machen: Es kommt immer nur eine Liste der Feldwerte für den Datensatz.

Objekte und Methoden

Und da wird’s Zeit für einen Schlenker, den zweiten Standard in meinem Projekt: Objektorientiertes Programmieren. Es geht heute nicht mehr ohne – es ist trotz aller Unkenrufe und Abgesänge Stand der Technik. Ich kombiniere dieses Programmierparadigma gerne mit einem zweiten, nämlich Domain Driven Design, dann wird es leichter greifbar. Didaktiker würden Domain Driven Design wohl „Lebensweltbezug“ nennen.

Ich konstruiere meine Programme also so, dass sie etwas repräsentieren, was im Umfeld (Domain) der Anwendung tatsächlich konkret existiert. Allgemeiner ist dieses Umfeld voll mit Dingen – Objekten. Lebewesen sind in diesem Sinne auch Objekte.

Mein erstes Objekt ist also eine Person. Das ist relativ naheliegend. Es wird definiert durch eine Klasse – gewissermaßen den Bauplan des Objekts. Dieser ist nicht komplett frisch geschrieben, sondern in Grundzügen vorgebaut, nämlich durch CI_Model. Das nennt man technisch „Vererbung“. Personen sind Menschen, Menschen sind Säugetiere, Säugetiere sind Lebewesen. Und ihr selbst seid das Kind Eurer Eltern, die wiederum Kinder Eurer Großeltern sind. Genau das passiert hier: So, wie eine echte Person eine Instanz von „Mensch“ ist, ist unsere programmierte Person eine Instanz von CI_Model, dem allgemeinen Datenmodell. Wie genau diese Person aussieht – man weiß es nicht. Das nennt man Polymorphismus. Jedes Mal, wenn ich eine neue Person instanziiere, kann diese anders aussehen.

Was aber immer gleich ist, sind ihre Methoden (das sind sozusagen Fähigkeiten). Eine solche Methode wäre bei echten Menschen: „benutze das Telefon“ mit dem Argument: Telefonnummer. Ist die Telefonnummer leer, benutzt der Mensch das Telefon für Angry Birds, ansonsten wählt er die Telefonnummer.

Unsere „Person“ hat eine Methode: Liefere Daten von Personen, und zwar der Person x oder allen Personen.

Und das ist im Kern einerseits das, was im Model passiert, und warum und wie es passiert. Nächster Teil: Der Controller.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*