Development

Documentation/de_DE/book/1.0/02-Exploring-Symfony-s-Code

You must first sign up to be able to contribute.

Version 7 (modified by Jan.Kunzmann, 10 years ago)
--

ARBEITSENTWURF ROHÜBERSETZUNG / TRANSLATION WORKING DRAFT
Originaldokument: ?, Version ? vom ?
Übersetzungsfortschritt: 100%
Übersetzung: ED
Korrekturfortschritt: 0%

Kapitel 2 - Erkundung des Symfony-Quelltexts

Auf den ersten Blick kann der Quelltext einer mit Symfony entwickelten Anwendung recht einschüchternd sein. Er besteht aus vielen Verzeichnissen und Skripten, und die Dateien sind eine Mischung aus PHP-Klassen, HTML, und sogar Mischformen aus beiden. Sie werden Referenzen auf Klassen entdecken, die nirgends im Anwendungsordner zu finden sind, und die Tiefe der Verzeichnisstruktur beträgt sechs Ebenen. Aber wenn Sie einmal den Grund für die ganze scheinbare Komplexität verstehen, werden Sie die Symfony-Struktur als so normal ansehen, dass Sie sie gegen keine andere eintauschen möchten. Dieses Kapitel wird den einschüchternden Eindruck aufklären.

Das MVC-Architekturmuster

Symfony basiert auf dem klassischen Webanwendungsdesign, bekannt als MVC-Architektur, welches aus den folgenden drei Ebenen besteht:

  • Das Datenmodell (model) repräsentiert die Informationen, mit denen die Anwendung arbeitet--die Geschäftslogik.
  • Die Präsentation (view) stellt das Datenmodell auf einer Webseite dar, um die Interaktion mit dem Benutzer zu ermöglichen.
  • Die Steuerung (controller) ragiert auf Benutzeraktionen und veranlasst Änderungen des Datenmodells oder der Präsentation soweit erforderlich.
    Abbildung 2-1 zeigt das MVC-Architekturmuster.

Die MVC-Architektur trennt die Geschäftslogik (model) und die Darstellung, was eine größere Wartungsfreundlichkeit bedeutet. Wenn Ihre Anwendung zum Beispiel sowohl für Standard-Browser als auch mobile Endgeräte geeignet sein soll, benötigen Sie lediglich eine neue Präsentation; Die ursprüngliche Steuerung und das Datenmodell bleiben erhalten. Die Steuerung dient dazu, Details des anfragenden Protokolls (HTTP, Konsole, E-Mail, usw.) vor dem Datenmodell und der Präsentation zu verbergen. Das Datenmodell wiederum abstrahiert die Logik der Daten, was dafür sorgt, dass die Präsentation und die Vorgänge der Anwendung zum Beispiel von der verwendeten Datenbank unabhängig sind.

Abbildung 2-1 - Das MVC-Architekturmuster

Das MVC-Architekturmuster

MVC Schichten

Um Ihnen zu helfen, die Vorteile von MVC zu verstehen, schauen wir uns die Umwandlung einer einfachen PHP-Anwendung in eine MVC-basierende Anwendung an. Eine Liste von Beiträgen in einer Weblog-Anwendung bieten ein perfektes Beispiel.

Flache Programmierung

In einer "flachen" PHP-Datei könnte das Darstellen einer Liste von Datenbankeinträgen wie das Skript im Codeabschnitt 2-1 aussehen.

Codeabschnitt 2-1 - Ein flaches PHP-Skript

[php]
<?php

// zur Datenbank verbinden, Datenbank auswählen
$link = mysql_connect('localhost', 'myuser', 'mypassword');
mysql_select_db('blog_db', $link);

// SQL-Abfrage durchführen
$result = mysql_query('SELECT date, title FROM post', $link);

?>

<html>
  <head>
    <title>Liste der Beiträge</title>
  </head>
  <body>
   <h1>Liste der Beiträge</h1>
   <table>
     <tr><th>Datum</th><th>Titel</th></tr>
<?php
// die Ergebnisse als HTML ausgeben
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
echo "\t<tr>\n";
printf("\t\t<td> %s </td>\n", $row['date']);
printf("\t\t<td> %s </td>\n", $row['title']);
echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>

<?php

// Datenbankverbindung schließen
mysql_close($link);

?>

Solcher Quelltext ist schnell geschrieben, kann schnell ausgeführt werden, und es ist unmöglich ihn zu warten. Folgende Probleme ergeben sich aus dem Quelltext:

  • Es gibt keine Fehlerbehandlung (was passiert, wenn die Datenbankverbindung nicht aufgebaut werden kann?).
  • HTML und PHP sind gemischt, ja sogar ineinander verwoben.
  • Der Quelltext ist an eine MySQL-Datenbank gebunden.

Die Darstellung isolieren

Die Aufrufe von echo und printf im Codeabschnitt 2-1 machen den Quelltext schwer lesbar. Eine Änderung des HTML-Codes zur Verbesserung der Darstellung bereitet in dem momentanen Skript Schwierigkeiten. Der Quelltext kann in zwei Teile gegliedert werden. Der reine PHP-Code mit der Geschäftslogik wandert in ein Steuerungsskript, wie z.B. im Codeabschnitt 2-2.

Codeabschnitt 2-2 - Der Steuerungsteil, in index.php

[php]
<?php

// zur Datenbank verbinden, Datenbank auswählen
 $link = mysql_connect('localhost', 'myuser', 'mypassword');
 mysql_select_db('blog_db', $link);

// SQL-Abfrage durchführen
 $result = mysql_query('SELECT date, title FROM post', $link);

 // Füllen eines Arrays für die Darstellung
 $posts = array();
 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
 {
    $posts[] = $row;
 }

// Datenbankverbindung schließen
 mysql_close($link);

 // die Darstellung wird benötigt
 require('view.php');

 ?>

Der HTML-Code, der eine Template-ähnliche PHP-Syntax enthält, wird in einem Skript zur Darstellung wie in Codeabschnitt 2-3 abgespeichert.

Codeabschnitt 2-3 - Die Darstellung, in view.php

[php]
<html>
  <head>
    <title>Liste der Beiträge</title>
  </head>
  <body>
    <h1>Liste der Beiträge</h1>
    <table>
      <tr><th>Datum</th><th>Titel</th></tr>
    <?php foreach ($posts as $post): ?>
      <tr>
        <td><?php echo $post['date'] ?></td>
        <td><?php echo $post['title'] ?></td>
      </tr>
    <?php endforeach; ?>
    </table>
  </body>
</html>

Als einfache Faustregel um festzustellen, ob die Darstellung "sauber genug" ist, sollte man untersuchen, ob der Quelltext nur ein Minimum an PHP-Code enthält, damit auch ein HTML-Designer ohne PHP-Wissen den Quelltext verstehen kann. Die gewöhnlichsten PHP-Aufrufe in der Darstellung sind echo, if/endif, foreach/endforeach, mehr nicht. Es sollte auch keinen PHP-Code geben, der HTML-Tags ausgibt.

Die gesamte Geschäftslogik wird in das Steuerungsskript ausgelagert, dieses enthält reinen PHP-Code, ohne jegliches HTML. Sie sollten sich vorstellen, dass dieses Steuerungsskript tatsächlich für eine völlig andere Darstellung wiederverwendet werden könnte, z.B. zur Ausgabe einer PDF-Datei oder von XML-Daten.

Die Datenschicht isolieren

Der Großteil des Steuerungsskripts dient der Datenmanipulation. Was würden Sie jedoch tun, wenn Sie die Liste der Beiträge in einem anderen Steuerungsskript benötigen, z.B. eines, das einen RSS-Feed der Weblog-Beiträge ausgibt? Was würden Sie tun, wenn Sie alle Datenbankabfragen an einer Stelle haben wollen, um doppelten Quelltext zu vermeiden? Was würden Sie tun, wenn Sie sich entscheiden das Datenmodell so zu ändern, dass die Tabelle post in weblog_post umbenannt wird? Was würden Sie tun, wenn Sie statt MySQL PostgreSQL einsetzen möchten? Um all diese Änderungen zu ermöglichen, müssen Sie den Quelltext zur Datenmanipulation aus dem Steuerungsskript entfernen und in einem separaten Skript, dem Datenmodell, zusammenfassen, wie im Codeabschnitt 2-4.

Codeabschnitt 2-4 - Das Datenmodell, in model.php

[php]
<?php

function getAllPosts()
{
  // zur Datenbank verbinden, Datenbank auswählen
  $link = mysql_connect('localhost', 'myuser', 'mypassword');
  mysql_select_db('blog_db', $link);

  // SQL-Abfrage durchführen
  $result = mysql_query('SELECT date, title FROM post', $link);

  // Füllen eines Arrays
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }

  // Datenbankverbindung schließen
  mysql_close($link);

  return $posts;
}

?>

Das überarbeitete Steuerungsskript ist im Codeabschnitt 2-5 dargestellt.

Codeabschnitt 2-5 - The Steuerung, überarbeitet, in index.php

[php]
<?php

// das Datenmodell wird benötigt
require_once('model.php');

// Abfragen der Liste der Beiträge
$posts = getAllPosts();

// die Darstellung wird benötigt
require('view.php');

?>

Das Steuerungsskript wird leichter lesbar. Seine einzige Aufgabe ist es, die Daten vom Datenmodell zu holen und an die Darstellung weiterzugeben. In komplexeren Anwendungen handhabt die Steuerung auch Requests, die Benutzersitzung, die Authentifizierung, und so weiter. Die Verwendung von sprechenden Namen für die Funktionen des Datenmodell machen Kommentare im Quelltext der Steuerung sogar überflüssig.

Das Skript des Datenmodells dient dem Zugriff auf die Daten und kann dementsprechend organisiert werden. Alle Parameter, die nicht vom Datenmodell abhängen (wie Parameter eines Requests), müssen von der Steuerung übergeben werden und sollten nicht direkt vom Datenmodell abgefragt werden. Die Funktionen des Datenmodells können leicht von einem anderen Steuerungsskript aus wiederverwendet werden.

Trennung der Schichten über MVC hinaus

Das Prinzip des MVC-Architekturmusters ist es also, den Quelltext in drei Ebenen gemäß ihres Zwecks zu unterteilen. Der Quelltext bezüglich der Geschäftslogik der Daten befindet sich im Datenmodell, Quelltext zur Darstellung wird in der Darstellung abgelegt, und die Anwendungslogik findet sich in der Steuerung wieder.

Weiter zusätzliche Architekturmuster können das Programmieren sogar noch mehr erleichtern. Das Datenmodell, die Darstellung und die Steuerungsschichte können weiter unterteilt werden.

Datenbankabstraktion

Das Datenmodell kann in eine Datenzugriffsschicht und eine Datenbankabstraktionsschicht untergliedert werden. Auf diese Weise verwenden Funktionen des Datenzugriffs keine datenbankspezifische Syntax, sondern rufen andere Funktionen auf, die die Abfragen durchführen. Wenn das Datenbanksystem später geändert wird, muss nur die Datenbankabstraktionsschicht angepasst werden.

Ein Beispiel einer MySQL-spezifischen Datenbankabstraktionsschicht ist im Codeabschnitt 2-6 dargestellt, gefolgt von einem Beispiel einer Datenzugriffsschicht im Codeabschnitt 2-7.

Codeabschnitt 2-6 - Die Datenbankabstraktionsschicht des Datenmodell

[php]
<?php

function open_connection($host, $user, $password)
{
  return mysql_connect($host, $user, $password);
}

function close_connection($link)
{
  mysql_close($link);
}

function query_database($query, $database, $link)
{
  mysql_select_db($database, $link);

  return mysql_query($query, $link);
}

function fetch_results($result)
{
  return mysql_fetch_array($result, MYSQL_ASSOC);
}

Codeabschnitt 2-7 - Die Datenzugriffsschicht des Datenmodells

[php]
function getAllPosts()
{
  // zur Datenbank verbinden
  $link = open_connection('localhost', 'myuser', 'mypassword');

  // SQL-Abfrage durchführen
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);

  // Füllen eines Arrays
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }

  // Datenbankverbindung schließen
  close_connection($link);

  return $posts;
}

?>

Sie können überprüfen, dass keine datenbankabhängige Funktion in der Datenzugriffsschicht zu finden ist, sie ist datenbankunabhängig. Darüber hinaus können die Funktionen, die in der Datenbankabstraktionsschicht erstellt wurden, für viele andere Funktionen des Datenmodells, die auf die Datenbank zugreifen müssen, wiederverwendet werden.

NOTE Die Beispiele im Codeabschnitt 2-6 und 2-7 sind immernoch nicht sehr zufriedenstellend, und es gibt noch einiges zu tun, um eine vollständige Abstraktion der Datenbank zu erreichen (Abstraktion der SQL-Abfragen durch einen datenbankunabhängigen Abfragegenerator, die Zusammenfassung aller Funktionen in einer Klasse, usw). Der Zweck dieses Buches ist es jedoch nicht Ihnen zu zeigen, wie Sie sämtlichen Quelltext selber schreiben, und Sie werden im Kapitel 8 feststellen, dass Symfony von Haus aus die ganze Abstraktion sehr gut beherrscht.

Elemente der Darstellung

Die Darstellungsschicht kann auch von einer weiteren Unterteilung des Quelltextes profitieren. Eine Webseite enthält oft einheitliche Elemente in der gesamten Anwendung: Den Seitenkopf, das grafische Layout, die Fußzeile, und die globale Navigation. Nur der innere Bereich der Seite ändert sich. Deshalb wird die Darstellung in ein Layout und eine Vorlage unterteilt. Das Layout wird normalerweise für die gesamte Anwendung oder eine Gruppe von Seiten verwendet. Die Vorlage schließt nur die von der Steuerung zur Verfügung gestellten Variablen ein. Es ist einiges an Logik notwendig, um das Zusammenspiel dieser Komponenten zu ermöglichen, und diese Darstellungsschicht wird die Bezeichnung Darstellung behalten. Gemäß dieser Prinzipien kann die Darstellung aus Codeabschnitt 2-3 in drei Teile unterteilt werden, wie in den Codeabschnitts 2-8, 2-9 und 2-10 zu sehen ist.

Codeabschnitt 2-8 - Das Template, in mytemplate.php

[php]
<h1>Liste der Beiträge</h1>
<table>
<tr><th>Datum</th><th>Titel</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Codeabschnitt 2-9 - Die Darstellung

[php]
<?php

$title = 'Liste der Beiträge';
$content = include('mytemplate.php');

?>

Codeabschnitt 2-10 - Das Layout

[php]
<html>
  <head>
    <title><?php echo $title ?></title>
  </head>
  <body>
    <?php echo $content ?>
  </body>
</html>

Action und Front Controller

Die Steuerung (Controller) erledigt im vorangegangenen Beispiel nicht viele Aufgaben, aber in echten Webanwendungen hat die Steuerung viel zu tun. Ein wichtiger Teil dieser Aufgaben ist allen Steuerungsskripten der Anwendung gemein. Diese Standardaufgaben umfassen die Handhabung von Anfragen, Sicherheit, Laden der Konfiguration, und ähnliche Routinearbeiten. Deshalb wird die Steuerung meist in einen Front Controller, der in der gesamten Anwendung nur einmal vorkommt, und in Actions (Aktionen), die nur die spezifischen Steuerungsfunktionen einer Seite enthalten, unterteilt.

Einer der größten Vorteile des Front Controllers ist, dass er einen eindeutigen Einstiegspunkt in die gesamte Anwendung darstellt. Wenn Sie beschließen den Zugriff auf ihre Anwendung zu unterbinden, müssen Sie lediglich den Front Controller bearbeiten. In einer Anwendung ohne Front Controller müsste jeder einzelne Controller deaktiviert werden.

Objektorientierung

Alle vorhergehenden Beispiele verwenden prozeduralen Programmierung. Die OOP-Fähigkeiten moderner Programmiersprachen vereinfachen die Programmierung, da Objekte Logik enthalten können, voneinander erben können, und für einheitliche Namenskonventionen sorgen können.

Die Implementierung einer MVC-Architektur in einer Programmiersprache, die keine Objektorientierung unterstützt, bereitet Probleme in Hinsicht auf Namensräume und doppelten Quelltext, und der gesamte Quelltext ist schwer zu lesen.

Objektorientierung erlaubt es Entwicklern alle Aufgaben mit einem Darstellungs-, Steuerungs- und Datenmodellobjekt zu bewältigen und alle Funktionen der vorhergehenden Beispiele in Methoden umzuwandeln. Objektorientierung ist ein Muss für das MVC-Architekturmodell.

TIP Wenn Sie mehr über Architekturmuster für Webanwendungen unter dem Aspekt der Objektorientierung erfahren möchten, lesen Sie "atterns of Enterprise Application Architecture" von Marin Fowler (Addison-Wesley, ISBN: 0-32112-742-0). Quelltextbeispiele in Fowlers Buch sind in Java oder C# geschrieben, sind aber dennoch recht lesbar für PHP-Entwickler.

Symfonys MVC-Implementierung

Warten Sie einen Augenblick. Wieviele Komponenten werden benötigt, um eine einzelne Seite mit einer Liste der Beiträge eines Weblogs anzuzeigen? Wie in Grafik 2-2 dargestellt, haben wir die folgenden Elemente:

  • Datenmodellschicht
    • Datenbankabstraktion
    • Datenzugriff
  • Darstellungsschicht
    • Darstellung
    • Vorlage
    • Layout
  • Steuerungsschicht
    • Front Controller
    • Aktionen

Sieben Skripte--eine Menge Skripte, die bei jedem Anlegen einer neuen Seite geöffnet und angepasst werden müssen! Allerdings erleichtert Symfony die Arbeit. Obwohl Symfony das Beste des MVC-Architekturmodells beinhaltet, implementiert es diese Modell auf eine Weise, die die Anwendungsentwicklung schnell und schmerzlos macht.

Zunächst einmal haben alle Aktionen einer Anwendung den Front Controller und das Layout gemeinsam. Sie können mehrere Controller und Layouts haben, aber Sie brauchen nur jeweils einen. Der Front Controller ist eine reine Logikkomponente von MVC, und Sie werden nie einen programmieren müssen, weil Symfony ihn automatisch erzeugt.

Die nächste gute Nachricht ist, dass die Klassen des Datenmodells auch automatisch erzeugt werden, und zwar basierend auf Ihrer Datenstruktur. Das ist die Aufgabe der Propel-Bibliothek, die Klassengerüste und Quelltextgenerierung bietet. Wenn Propel Fremdschlüssel oder Datumsfelder findet, wird es spezielle Zugriffs- und Bearbeitungsfunktionen zur Verfügung stellen, die die Datenmanipulation leicht machen. Die Datenbankabstraktion ist für Sie völlig transparent, da Sie von einer anderen Komponente erledigt wird, Creole. Wenn Sie also zu einem Zeitpunkt das Datenbanksystem wechseln wollen, müssen Sie keinen Quelltext erneut schreiben, sondern lediglich einen Konfigurationsparameter ändern.

Der letzte Punkt ist, dass die Darstellungslogik ganz leicht durch einfache Konfigurationsdateien erzeugt werden kann, ohne jegliche Programmierung.

Abbildung 2-2 - Symfony Workflow

Symfony Workflow

Das bedeutet, dass die Liste der Beiträge, die wir in unserem Beispiel behandelt haben, mit Symfony nur drei Dateien benötigt, um zu funktionieren, dargestellt in den Codeabschnitts 2-11, 2-12 und 2-13. That means that the list of posts described in our example would require only three files to work in symfony, as shown in Codeabschnitts 2-11, 2-12, and 2-13.

Codeabschnitt 2-11 - list Aktion, in myproject/apps/myapp/modules/weblog/actions/actions.class.php

[php]
<?php
class weblogActions extends sfActions
{
  public function executeList()
  {
    $this->posts = PostPeer::doSelect(new Criteria());
  }
}

?>

Codeabschnitt 2-12 - list Vorlage, in myproject/apps/myapp/modules/weblog/templates/listSuccess.php

[php]
<h1>Liste der Beiträge</h1>
<table>
<tr><th>Datum</th><th>Titel</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Codeabschnitt 2-13 - list Darstellung, in myproject/apps/myapp/modules/weblog/config/view.yml

listSuccess:
  metas: { title: Liste der Beiträge }

Darüber hinaus müssen Sie immer noch ein Layout festlegen, wie im Codeabschnitt 2-14, diese jann jedoch mehrfach wiederverwendet werden.

Codeabschnitt 2-14 - Layout, in myproject/apps/myapp/templates/layout.php

[php]
<html>
  <head>
    <?php echo include_title() ?>
  </head>
  <body>
    <?php echo $sf_data->getRaw('sf_content') ?>
  </body>
</html>

Das ist wirklich alles, was Sie benötigen. Das ist genau der Quelltext, den Sie benötigen, um genau die gleiche Seite wie mit dem "flachen" Skript aus Codeabschnitt 2-1 darzustellen. Den Rest (das Zusammenspiel aller Komponenten) erledigt Symfony. Wenn Sie die Anzahl der Quelltextzeilen zur Darstellung einer List von Beiträgen zählen, werden Sie feststellen, dass die Darstellung mittels Symfony und des MVC-Architekturmodells nicht viel mehr Zeit und Quelltext erfordert als mittels eines "flachen" Skripts. Gleichwohl birgt das Vorgehen enorme Vorteile, vor allem klare Quelltextorganisation, Wiederverwendbarkeit, Flexibilität, und viel mehr Spaß. Und als Dreingabe erhalten Sie XHTML-Konformität, Debug-Möglichkeiten, einfache Konfiguration, Datenbankabstraktion, elegante URLs, mehrere Anwendungsumgebungen, und noch viel mehr Entwicklungswerkzeuge.

Symfonys Hauptklassen

Die MVC-Implementation in Symfony verwendet einige Klassen, die Sie recht häufig in diesem Buch finden werden:

  • sfController ist die Steuerungsklasse. Sie versteht die Anfrage und gibt Sie an die Aktionen weiter.
  • sfRequest speichert alle Elemente einer Anfrage( Parameter, Cookies, Header, usw).
  • sfResponse enthält die Antwort-Header und den Inhalt. Das ist das Objekt, das eventuell eine HTML-Antwort erzeugt, die an den Benutzer gesendet wird.
  • Das Singleton Kontext (abgerufen mit sfContext::getInstance()) speichert eine Referenz zu allen Hauptklassen und zur aktuellen Konfiguration; man kann von überall darauf zugreifen.

Im Kapitel 6 werden Sie mehr über diese Objekte erfahren.

Wie Sie sehen, haben alle Symfony-Klassen das Präfix sf, genauso wie die Symfony-Variablen in den Templates. Dieses Vorgehen soll Namenskollisionen mit Ihren eigenen Klassen und Variablen verhindern, und macht die Hauptklassen des Frameworks einfach erkennbar und abfragbar.

NOTE Zu den Programmierungsrichtlinien in Symfony zählt UpperCamelCase als Standard zur Benennung von Klassen und Variablen. Es gibt zwei Ausnahmen: die Hauptklassen von Symfony beginnen mit sf, also Kleinbuchstaben, und Variablen in Vorlagen verwenden die "underscore-separated syntax", d.h. Unterstriche zur Trennung der Namensteile von Variablen bzw. Klassen.

Quelltextorganisation

Da Sie nun die verschiedenen Komponenten einer Symfony-Anwendung kennen, fragen Sie sich sicherlich, wie diese organisiert sind. Symfony organisiert den Quelltext in einer Projektstruktur, die Projektdateien werden in einer normalen Baumstruktur abgelegt.

Projektstruktur: Anwendungen, Module, Aktionen

Symfony versteht ein Projekt als eine Menge von Diensten und Funktionalitäten, die einer festgelegten Domäne verfügbar sind, und auf das gleiche Objektmodell zugreifen.

Innerhalb eines Projekts werden die Funktionalitäten in Anwendungen logisch zusammengefasst. Eine Anwendung kann normalerweise unabhängig von den anderen des gleichen Projekts ausgeführt werden. In den meisten Fällen wird ein Projekt zwei Anwendungen enthalten: eine für das Front-Office und eine für das Back-Office, wobei beide auf die gleiche Datenbank zugreifen. Sie können jedoch auch ein Projekt mit vielen kleinen Seiten erzeugen, mit jeder kleinen Seite als eine Anwendung. Beachten Sie, dass Hyperlinks zwischen Anwendungen absolut angegeben werden müssen.

Jede Anwendung besteht aus einem oder mehreren Modulen. Ein Modul umfasst eine Seite oder eine Gruppe von Seiten mit einem änhlichen Zweck. Sie haben zum Beispiel die Module home, articles, help, shoppingCart, account, usw.

Module enthalten Aktionen, die die verschiedenen Funktionen eines Moduls repräsentieren. Ein shoppingCart Modul hat zum Beispiel die Aktionen add, show und update. Üblicherweise kann eine Aktion mit einem Verb beschrieben werden. Die Arbeit mit Aktionen ist fast genauso wie die Arbeit mit Seiten in einer klassischen Webanwendung, wobei zwei Aktionen die gleiche Ausgabe hervorrufen können (zum Beispiel wird beim Hinzufügen eines Kommentars zu einem Beitrag in einem Weblog der Beitrag erneut angezeigt, nun mit dem Kommentar).

TIP Wenn dieses Vorgehen zu viele Ebenen für ein Einstiegsprojekt umfasst, können der Einfachheit halber alle Aktionen in einem einzigen Modul zusammengefasst werden, sodass die Dateistruktur einfach gehalten wird. Wenn die Anwendung komplexer wird, wird es Zeit die Aktionen auf mehrere Module aufzuteilen. Wie im Kapitel 1 erwähnt, wird das Neuschreiben von Quelltext zur Verbesserung seiner Struktur oder Lesbarkeit (bei gleicher Funktionalität) Refactoring genannt, und Sie werden diesen Schritt oft ausführen, wenn Sie RAD-Prinzipien einsetzen.

Grafik 2-3 zeigt die Quelltextorganisation am Beispiel eines Weblog-Projekts in einer Projekt-/Anwendungs-/Modul-/Aktionsstruktur. Die tatsächliche Dateistruktur des Projekts wird sich jedoch von der Aufteilung in der Grafik unterscheiden.

Figure 2-3 - Beispiel zur Quelltextorganisation

Beispiel zur Quelltextorganisation

Datei- und Verzeichnisstruktur

Alle Webanwendungen haben allgemein die gleichen Inhaltstypen gemeinsam, wie die folgenden:

  • Eine Datenbank wie MySQL oder PostgreSQL
  • Statische Dateien (HTML, Grafiken, Javascript-Dateien, Style Sheets, usw)
  • Dateien, die von Benutzern und Administratoren hochgeladen wurden
  • PHP-Klassen und Bibliotheken
  • Fremde Bibliotheken (Skripte Dritter)
  • Batch-Dateien (Skripte, die per Kommandozeile oder Cronjob ausgeführt werden)
  • Logdateien (Fehlerprotokollierungen, die von der Anwendung und/oder dem Server erzeugt werden)
  • Konfigurationsdateien

Symfony erstellt eine Standard-Dateistruktur um all diese Inhalte übersichtlich entsprechend der gewählten Architektur (MVC und Aufteilung in Projekt/Anwendung/Modul) abzulegen. Diese Dateistruktur wird bei der Initialisierung jedes Projekts, jeder Anwendung und jedes Moduls automatisch angelegt. Natürlich können Sie die Struktur beliebig anpassen, um die Dateien und Verzeichnisse gemäß Ihrer Wünsche anzuordnen oder um die Anforderungen des Kunden zu erfüllen.

Hauptverzeichnis

Folgende Verzeichnisse befinden sich im Hauptverzeichnis eines Symfony-Projekts:

apps/
  frontend/
  backend/
batch/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/

Tabelle 2-1 beschreibt die Inhalte dieser Verzeichnisse.

Tabelle 2-1 - Hauptverzeichnisse

Verzeichnis | Beschreibung ---------- | ------------ apps/ | Enthält ein Verzeichnis für jede Anwendung des Projekts (typischerweise frontend und backend für Front-Office und Back-Office). batch/ | Enthält PHP-Skripte, die von der Kommandozeile oder zeitgesteuert gestartet werden. Sie führen Stapelverarbeitungsaufgaben aus. cache/ | Enthält die zwischengespeicherte Version der Konfiguration, und (wenn Sie dieses Verhalten aktiviert haben) die zwischengespeicherten Versionen der Aktionen und Vorlagen des Projekts. Der Zwischenspeicherungsmechanismus (detailliert im Kapitel 12 beschrieben) verwendet diese Dateien, um die Antwortzeiten der Anwendung zu verbessern. Jede Anwendung besitzt in diesem Verzeichnis ein Unterverzeichnis, das vorverarbeitete PHP-Skripte und HTML-Dateien enthält. config/ | Beinhaltet die allgemeine Konfiguration des Projekts. data/ | In Diesem Verzeichnis können Sie Daten des Projekts speichern, wie zum Beispiel ein Datenbankschema, eine SQL-Datei, die die Tabellen erzeugt, oder sogar eine SQLite-Datenbank. doc/ | Beinhaltet die Projektdokumentation inklusive Ihrer eigenen Dokumente und der Dokumentation, die durch PHPdoch erzeugt wird. lib/ | Für weitere Klassen und Bibliotheken bestimmt. Hier können Sie den Quelltext ablegen, der von Ihren Anwendungen gemeinsam genutzt werden soll. Das Unterverzeichnis model/ enthält das Objektmodell des Projekts (im Kapitel 8 näher erläutert). log/ | Die Log-Dateien, die von Symfony erzeugt werden, sind hier gespeichert. Es können auch Log-Dateien des Webservers, des Datenbanksystems oder eines beliebigen anderen Teils des Projekts hier abgelegt werden. Symfony erzeugt je Anwendung und Umgebung eine Log-Datei (Log-Dateien werden im Kapitel 16 behandelt). plugins/ | Die installierten Plugins der Anwendung werden in diesem Verzeichnis abgelegt (um Plugins geht es im Kapitel 17). test/ | Unit Tests und funktionale Tests, die in PHP geschrieben wurden und kompatibel mit dem Symfony Test-Framework sind (siehe Kapitel 15), befinden sich in diesem Verzeichnis. Während der Initialisierung des Projekts erstellt Symfony automatisch einige einfache Klassen mit grundlegenden Tests. web/ | Das Hauptverzeichnis für den Webserver. Nur auf die Dateien in diesem Verzeichnis kann vom Internet aus zugegriffen werden.

Dateisystemstruktur einer Anwendung

Die Dateisystemstruktur aller Anwendungen ist die gleiche:

apps/
  [Anwendungsname]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
      error.php
      error.txt

Tabelle 2-2 beschreibt die Unterverzeichnisse des Anwendungsverzeichnisses.

Tabelle 2-2 - Unterverzeichnisse des Anwendungsverzeichnisses

Verzeichnis | Beschreibung ------------ | ----------- config/ | Enthält eine Menge YAML-Konfigurationsdateien. Hier befindet sich der Großteil der Konfiguration einer Anwendung, abgesehen von den Standardvorgaben des Frameworks selbst. Beachten Sie, dass die Standardvorgaben hier immer noch bei Bedarf überschrieben werden können. Sie werden in Kapitel 5 mehr über die Anwendungskonfiguration erfahren. i18n/ | Die Dateien zur Internationalisierung der Anwendung befinden sich in diesem Verzeichnis--hauptsächlich Übersetzungsdateien (Kapitel 13 behandelt die Internationalisierung). Sie können dieses Verzeichnis übergehen, wenn Sie eine Datenbank zur Internationalisierung verwenden. lib/ | Enthält Klassen und Bibliotheken, die zu dieser Anwendung gehören. modules/ | Alle Module, welche die Funktionalität der Anwendung enthalten, sind hier gespeichert. templates/ | Beinhaltet die globalen Vorlagen der Anwendung--und jene, die von allen Modulen genutzt werden. Standardmäßig enthält der Ordner eine Datei layout.php, welche das Hauptlayout darstellt und in welchem alle Vorlagen dargestellt werden.

NOTE Die Verzeichnisse i18n/, lib/ und modules/ sind in einer neuen Anwendung leer.

Die Klassen einer Anwendung können nicht auf Methoden oder Attribute in anderen Anwendungen des gleichen Projekts zugreifen. Beachten Sie auch, dass Hyperlinks zwischen zwei Anwendungen des gleichen Projekts absolut sein müssen. Behalten Sie diese Beschränkung bei der Initialisierung im Hinterkopf, insbesondere wenn Sie entscheiden, wie Sie Ihr Projekt in Anwendungen unterteilen.

Dateisystemstruktur eines Moduls

Jede Anwendung enthält ein oder mehrere Module. Jedes Modul befindet sich in einem eigenen Unterverzeichnis im Ordner modules, der Name des Unterverzeichnisses wird bei der Einrichtung des Moduls festgelegt.

So sieht die typische Ordnerstruktur eines Moduls aus:

apps/
  [Anwendungsname]/
    modules/
      [Modulname]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

Tabelle 2-3 beschreibt die Unterverzeichnisse des Modulverzeichnisses.

Table 2-3 - Unterverzeichnisse des Modulverzeichnisses

Verzeichnis | Beschreibung ------------ | ------------ actions/ | Enthält gewöhnlich eine einzige Klassendatei namens actions.class.php, in der alle Aktionen eines Moduls enthalten sind. Sie können den Quelltext verschiedener Aktionen auch in verschiedenen Dateien ablegen. config/ | Kann angepasste Konfigurationsdateien mit lokalen Parametern des Moduls enthalten. lib/ | Beinhaltet Klassen und Bibliotheken, die zum Modul gehören. templates/ | Enthält die Vorlagen zu den Aktionen des Moduls. Eine Standardvorlage namens indexSuccess.php wird bei der Einrichtung des Moduls angelegt. validate/ | Für Konfigurationsdateien zur Plausibilitätsprüfung von Formularen (in Kapitel 10 behandelt).

NOTE Die Verzeichnisse config/, lib/ und validate/ sind in einem neuen Modul leer.

Struktur des Hauptverzeichnisses für den Webserver

Es gibt wenige Einschränkungen für das Verzeichnis web, welches das Verzeichnis für öffentlich zugängliche Dateien ist. Das Einhalten von einigen wenigen Namenskonventionen sorgt für nützliche Abkürzungen und Standardverhalten in den Vorlagen. Hier ein Beispiel der Ordnerstruktur des Verzeichnisses web:

web/
  css/
  images/
  js/
  uploads/

Gewöhnlich werden die statischen Dateien in den Verzeichnissen gemäß Tabelle 2-4 abgelegt.

Table 2-4 - Typische Unterverzeichnisse des Verzeichnisses web

Verzeichnis | Beschreibung ---------- | ----------- css/ | Enthält die Stylesheets mit der Erweiterung .css. images/ | Enthält Bilder in den Formaten .jpg, .png, oder .gif. js/ | Beinhaltet JavaScript-Dateienmit der Erweiterung .js. uploads/ | Muss die Dateien enthalten, die von den Benutzern hochgeladen wurden. Obwohl das Verzeichnis normalerweise Bilder enthält, ist es vom Bilderverzeichnis getrennt, damit die Synchronisation zwischen Entwicklungsumgebung und Produktivsystem nicht die hochgeladenen Bilder in Mitleidenschaft zieht.

NOTE Obwohl es sehr empfohlen wird, die Standardstruktur beizubehalten, ist es möglich, die Struktur an Ihre Bedürfnisses anzupassen, zum Beispiel kann ein Projekt auf einem Server mit einer anderen Ordnerstruktur laufen. Mehr Informationen zur Änderung der Dateisystemstruktur finden Sie im Kapitel 19.

Gängige Werkzeuge

Einige Techniken werden wiederholt in Symfony angewendet und Sie werden dieses recht oft in diesem Buch und in Ihren eigenen Projekten antreffen. Diese Rechniken umfassen Objekte, die Parameter bereithalten (Parameter Holder), Konstanten, und das automatische Laden von Klassen.

Parameter-Klassen (Parameter Holder)

Viele der Klassen in Symfony besitzen ein Objekt, dass Parameter bereithält. Es handelt sich hierbei um einen sauberen Weg, um Attribute mit sauberen get und set Methoden zu kapseln. Die Klasse sfResponse zum Beispiel besitzt einen Parameter Holder, auf den mit der Methode getParameterHolder() zugegriffen werden kann. Jeder Parameter Holder hält Daten auf die gleiche Art und Weise bereit, wie das Codeabschnitt 2-15 zeigt.

Codeabschnitt 2-15 - Verwendung des Parameter Holder sfResponse

[php]
$response->getParameterHolder()->set('foo', 'bar');
echo $response->getParameterHolder()->get('foo');
 => 'bar'

Die meisten Klassen, die einen Parameter Holder verwenden, besitzen Proxy-Methoden um den Zugriff auf die get/set Methoden abzukürzen. Das ist auch für das Objekt sfResponse der Fall, der Quelltext aus Codeabschnitt 2-16 macht das gleiche wie im Codeabschnitt 2-15.

Codeabschnitt 2-16 - Verwendung der Parameter von sfResponse über Proxy-Methoden

[php]
$response->setParameter('foo', 'bar');
echo $response->getParameter('foo');
 => 'bar'

Die get-Methode des Parameter Holders akzeptiert als zweites Argument einen Standardwert. Diese Verhalten bietet einen nützlichen Rückfallmechanismus, der viel übersichtlicher ist als die Umsetzung mit Bedingungen. Im Codeabschnitt 2-17 folgt ein Beispiel.

Codeabschnitt 2-17 - Verwendung eines Standardwerts in der get-Methode des Parameter Holders

[php]
// Der Parameter 'foobar' ist nicht definiert, die get-Methode gibt somit einen leeren Wert zurück
echo $response->getParameter('foobar');
 => null

// Ein Standardwert kann erreicht werden, indem die get-Methode in einer Bedingung auftaucht
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'default';
}
 => default

// Am schnellsten ist jedoch die Verwendung des zweiten Arguments der get-Methode
echo $response->getParameter('foobar', 'default');
 => default

Der Parameter Holder unterstützt sogar Namensräume. Wenn Sie ein drittes Argument für die set- und get-Methoden angeben, wird dieses als Namensraum verwendet, und der Parameter wird nur innerhalb dieses Namensraumes definiert. Codeabschnitt 2-18 zeigt ein Beispiel.

Codeabschnitt 2-18 - Verwendung von Namensräumen mit dem Parameter Holder von sfResponse

[php]
$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'my/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'my/name/space');
 => 'bar2'

Natürlich können Sie zu Ihren eigenen Klassen einen Parameter Holder hinzufügen, um sich die Syntaxvereinfachungen zu Nutze zu machen. Codeabschnitt 2-19 zeigt, wie eine Klasse mit einem Parameter Holder definiert wird.

Codeabschnitt 2-19 - Hinzufügen eines Parameter Holders zu einer Klasse

[php]
class MyClass
{
  protected $parameter_holder = null;

  public function initialize ($parameters = array())
  {
    $this->parameter_holder = new sfParameterHolder();
    $this->parameter_holder->add($parameters);
  }

  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
}

Konstanten

Überraschenderweise werden Sie wenige Konstanten in Symfony finden. Der Hauptgrund ist, dass Konstanten in PHP einen großen Nachteil haben: Sie können ihren Wert nicht ändern, nachdem Sie definiert wurden. Symfony verwendet daher sein eigenes Konfigurationsobjekt namens sfConfig, welches Konstanten ersetzt. Das Objekt bietet statische Methoden, um von überall auf Parameter zugreifen zu können. Codeabschnitt 2-20 demonstriert die Verwendung der Klasse sfConfig und ihrer Methoden.

Codeabschnitt 2-20 - Verwendung der Klasse sfConfig und ihrer Methoden statt Konstanten

[php]
// Statt PHP Konstanten
define('SF_FOO', 'bar');
echo SF_FOO;
// verwendet Symfony das Objekt sfConfig
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

Die Methoden von sfConfig unterstützen Standardwerte, und Sie können die Methode sfConfig::set() mehrfach für den gleichen Parameter aufrufen um seinen Wert zu ändern.

Automatisches Laden von Klassen

Normalerweise müssen Sie in PHP eine Klassendefinition zunächst einbinden, bevor Sie eine Methode einer Klasse verwenden oder ein Objekt anlegen können.

[php]
include 'classes/MyClass.php';
$myObject = new MyClass();

In großen Projekten mit vielen Klassen und verschachtelter Verzeichnisstruktur ist es jedoch zeitaufwändig die Übersicht zu behalten, welche Klassen eingebunden werden müssen und in welchem Verzeichnis sie sich befinden. Durch die Bereitstellung einer Funktion __autoload() (oder spl_autoload_register()) macht Symfony include Anweisungen überflüssig, und Sie können direkt folgende Anweisung ausführen:

[php]
$myObject = new MyClass();

Symfony sucht dann nach der Definition der Klasse MyClass in allen Dateien, die die Endung php besitzen und sich einem lib/ Verzeichnis im Projekt befinden. Wird die Klassendefinition gefunden, wird Sie automatisch eingebunden.

Wenn Sie also alle Ihre Klassen in lib/ Verzeichnissen speichern, müssen Sie keine Klassen mehr manuell einbinden. Das ist auch der Grund, warum Symfony-Projekte gewöhnlich keine include oder require Anweisungen mehr enthalten.

NOTE Zur Geschwindigkeitsoptimierung durchsucht Symfony bei der ersten Anfrage eine Liste von Verzeichnissen (diese ist in einer internen Konfigurationsdatei abgelegt). Alle Klassen, die diese Verzeichnisse enthalten, werden registriert, und die Zuweisung Klasse/Datei wird in einer PHP-Datei als assoziatives Array gespeichert. Auf diese Weise müssen bei weiteren Anfragen die Verzeichnisse nicht erneut durchsucht werden. Das ist auch der Grund, warum Sie den Cache jedes Mal mit dem Befehl symfony clear-cache leeren müssen, wenn Sie eine Klasse in Ihrem Projekt hinzufügen oder verschieben. Im Kapitel 12 erfahren Sie mehr zum Cache, die Konfiguration des automatischen Ladens von Klassen wird im Kapitel 19 behandelt.

Zusammenfassung

Die Verwendung eines Frameworks nach dem MVC-Architekturmuster zwingt Sie dazu, Ihren Quelltext gemäß den Vorgaben des Frameworks zu unterteilen und zu organisieren. Quelltext zur Ausgabe gehört zur Darstellung, Datenmanipulation zum Datenmodell, und die Bearbeitung der Anfragen gehört zur Steuerung. Die Anwendung des MVC-Architekturmusters ist sowohl sehr hilfreich als auch einschränkend.

Symfony ist ein in PHP 5 geschriebenes MVC-Framework. Die Struktur ist so aufgebaut, um das beste aus dem MVC-Architekturmuster herauszuholen, und das bei leichter Anwendbarkeit. Dank der Vielseitigkeit und Konfigurierbarkeit eignet sich Symfony für alle Webanwendungen.

Da Sie nun die Theorie, die Symfony zu Grunde liegt, verstanden haben, sind Sie fast bereit Ihre erste Anwendung zu entwickeln. Doch zunächst müssen Sie Symfony auf Ihrem Entwicklungsserver installieren.