Development

Documentation/de_DE/book/1.0/07-Inside-the-View-Layer

You must first sign up to be able to contribute.

ARBEITSENTWURF ROHÜBERSETZUNG / TRANSLATION WORKING DRAFT
Originaldokument: http://svn.symfony-project.com/doc/branches/1.1/book/07-Inside-the-View-Layer.txt, Version 7705 vom 2008-03-01
Übersetzungsfortschritt: 100%
Übersetzung: SK, JK
Korrekturfortschritt: 5%

Kapitel 7 - Die View-Ebene

Die Sicht (View) ist dafür verantwortlich, den korrekten Output einer bestimmten Aktion anzuzeigen. In Symfony besteht die Sicht aus mehreren Teilen, wobei jeder Teil so konzipiert wurde, dass er sich möglichst einfach verändern lässt von den Personen, die ihn normalerweise benutzen.

  • Webdesigner arbeiten normalerweise mit den Templates (die Präsentation der Daten der aktuellen Aktion) und mit dem Layout (das den gemeinsamen Code aller Seiten enthält). Diese sind in HTML geschrieben, das kleine PHP-Snippets enthält (meistens Helfer-Aufrufe).
  • Um der Wiederbenutzbarkeit willen stecken Entwickler normalerweise Templatecodefragmente in sogenannte Partials oder Komponenten. Sie benutzen Slots und Komponentenslots, um mehrere Bereiche des Layouts abzudecken. Webdesigner können ebenfalls an diesen Templatefragmenten arbeiten.
  • Entwickler konzentrieren sich auf die YAML-Sicht-Konfigurations-Datei (um die Eigenschaften der Antwort und anderer Interface-Elemente zu bearbeiten) und auf das Antwortobject. Beim Arbeiten mit Variablen in einem Template darf das Risiko von Cross-Site-Scripting (XSS) nicht außer Acht gelasen werden – um für die Sicherheit der Benutzerdaten garantieren zu können, ist ein gutes Verständnis von Escaping-Verfahren nötig.

Aber welche Rolle auch immer Sie dabei spielen, Sie werden hilfreiche Werkzeuge finden, die Ihnen bei der mühsamen Präsentation der Resultate einer Aktion behilflich sein können. Dieses Kapitel erläutert all diese Werkzeuge.

Templates

Codeabschnitt 7-1 zeigt ein typisches Symfony-Template. Es enthält HTML-Code und ein wenig PHP-Code, normalerweise, um auf Variablen zuzugreifen, die in der Aktion definiert wurden (mit $this->name = 'foo';) und auf Helfer.

Codeabschnitt 7-1 - Ein beispielhaftes indexSuccess.php-Template

[php]
<h1>Willkommen</h1>
<p>Willkommen zurück, <?php echo $name ?>!</p>
<ul>Was möchten Sie tun?
  <li><?php echo link_to('Die neusten Artikel lesen', 'article/read') ?></li>
  <li><?php echo link_to('Einen neuen Artikel schreiben', 'article/write') ?></li>
</ul>

Wie in Kapitel 4 erklärt ist diese alternative PHP-Syntax vorzuziehen, da sie die Templates für Nicht-PHP-Entwickler lesbarer macht. Sie sollten den PHP-Code in den Templates auf ein Minimum reduzieren, da diese Dateien diejenigen sind, die das GUI Ihrer Applikation darstellen und deshalb manchmal von einem anderen Team bearbeitet werden, das sich auf Präsentation spezialisiert hat und nicht auf Applikationslogik. Wenn also die Logik in der Aktion bleibt, ist es auch einfacher, mehrere Templates für eine einzige Aktion zu haben, ohne Codewiederholungen.

Helfer

Helfer sind PHP-Funktionen, die HTML-Code zurückgeben und in Templates benutzt werden können. Beispielsweise ist in Codeabschnitt 7-1 die link_to()-Funktion so ein Helfer. Manchmal ersparen einem Helfer einfach nur etwas Zeit, indem sie in Templates häufig benutzte Codeschnipsel bequem verpacken. Beispielsweise ist es sehr einfach, sich die Funktionsdefinition dieses Helfers zu überlegen:

[php]
<?php echo input_tag('nickname') ?>
 => <input type="text" name="nickname" id="nickname" value="" />

Er ist in Codeabschnitt 7-2 zu sehen.

Codeabschnitt 7-2 - Beispielhafte Helfer-Definition

[php]
function input_tag($name, $value = null)
{
  return '<input type="text" name="'.$name.'" id="'.$name.'"value="'.$value.'" />';
}

In Wirklichkeit ist die input_tag()-Funktion von Symfony etwas komplizierter, da sie einen dritten Parameter akzeptiert, um dem <input>-Tag weitere Attribute hinzuzufügen. Sie können die komplette Syntax und alle Möglichkeiten in der Online-API-Dokumentation (http://www.symfony-project.org/api/symfony.html) einsehen.

Meistens beinhalten Helfer aber eine gewisse Intelligenz und ersparen es Ihnen somit, langen und komplizierten Code zu tippen:

[php]
<?php echo auto_link_text('Bitte besuchen Sie unsere Website www.beispiel.de') ?>
 => Bitte besuchen Sie unsere Website <a href="http://www.beispiel.de">www.beispiel.de</a>

Helfer vereinfachen das Schreiben von Templates und produzieren den bestmöglichen HTML-Code hinsichtlich Performance und Zugänglichkeit. Sie können immer noch reines HTML verwenden, aber mit Helfern geht es normalerweise schneller.

TIPP Möglicherweise wundern Sie sich darüber, dass die Helfer nach der Unterstrich-Syntax benannt sind und nicht nach der camelCase-Konvention, die sonst überall in Symfony verwendet wird. Das ist deshalb so, weil Helfer Funktionen sind, und alle Kern-PHP-Funktionen benutzen die Unterstrich-Syntaxkonvention.

Helfer deklarieren

Die Symfony-Dateien mit den Helferdefinitionen werden nicht automatisch geladen (weil sie Funktionen enthalten, und keine Klassen). Helfer sind nach Einsatzzweck sortiert. Beispielsweise sind alle Helferfunktionen, die mit Text zu tun haben, in einer Datei TextHelper.php enthalten, die Text-Helfergruppe. Wenn Sie also in einem Template einen Helfer benötigen, müssen Sie zuvor im Template die entsprechende Helfergruppe laden mit der use_helper()-Funktion. Codeabschnitt 7-3 zeigt ein Template, das den auto_link_text()-Helfer benutzt, der Teil der Texthelfer-Gruppe ist.

Codeabschnitt 7-3 - Die Benutzung eines Helfers deklarieren

[php]
// Benutze eine bestimmte Helfergruppe in diesem Template
<?php use_helper('Text') ?>
...
<h1>Beschreibung</h1>
<p><?php echo auto_link_text($description) ?></p>

TIPP Wenn Sie mehr als eine Helfergruppe deklarieren müssen, fügen Sie entsprechend mehr Argumente zum use_helper()-Funktionsaufruf hinzu. Um etwa die Text- und die Javascript-Helfergruppen in einem Template zu laden, rufen Sie <?php echo use_helper('Text', 'Javascript') ?> auf.

Einige Helfer sind standardmäßig in jedem Template verfügbar, ohne entsprechende Deklaration. Es sind dies die Helfer der folgenden Gruppen:

  • Helfer: Benötigt für das Einbinden anderer Helfer (die use_helper()-Funktion ist selbst auch ein Helfer)
  • Tag: Grundsätzlicher Taghelfer, wird von beinahe jedem anderen Helfer benötigt
  • Url: Link- und URL-Management-Helfer
  • Asset: Helfer, die sich um den HTML-<head>-Teil kümmern und auf einfache Art und Weise Links zu externen Dateien bereitstellen. (Bilder, JavaScript, CSS-Dateien)
  • Partial: Helfer, die das Einbinden von Templatefragmenten ermöglichen
  • Cache: Manipulation gecacheter Codefragmente
  • Form: Formularhelfer

Die Liste der standardmäßig geladenen Helfer kann in der settings.yml-Datei eingestellt werden. Wenn Sie also wissen, dass Sie die Helfer der Gruppe Cache nicht benötigen oder dass Sie immer diejenigen der Textgruppe benötigen, verändern sie die standard_helpers-Einstellung entsprechend. Das wird Ihre Applikation etwas beschleunigen. Die ersten vier Helfer in der obigen Liste (Helper, Tag, Url und Asset) können Sie nicht entfernen, da Sie zwingend notwendig sind für das Funktionieren der Templateengine. Deshalb erscheinen sie auch nicht in der Liste der Standardhelfer.

TIPP Sollten Sie einmal einen Helfer außerhalb eines Templates benötigen, können Sie eine Helfergruppe von überall aus mit sfLoader::loadHelpers($helpers) laden, wobei $helpers ein Helfergruppenname oder ein Array aus Helfergruppennamen ist. Wenn Sie etwa den auto_link_text()-Helfer in einer Aktion benutzen wollen, müssen Sie zuerst sfLoader::loadHelpers('Text') aufrufen.

Häufig benutzte Helfer

Sie werden in späteren Kapiteln mehr über einige Helfer erfahren und wie sie Ihnen helfen können. Codeabschnitt 7-4 gibt einen kurzen Überblick über die Standardhelfer, die häufig benutzt werden, zusammen mit dem HTML-Code, den sie zurückgeben.

Codeabschnitt 7-4 - Gängige Standardhelfer

[php]
// Helper-Gruppe
<?php use_helper('Helfername') ?>
<?php use_helper('Helfername1', 'Helfername2', 'Helfername3') ?>

// Tag-Gruppe
<?php echo tag('input', array('name' => 'foo', 'type' => 'text')) ?>
<?php echo tag('input', 'name=foo type=text') ?>  // alternative Syntax für die Optionen
 => <input name="foo" type="text" />
<?php echo content_tag('textarea', 'Beispieltext', 'name=foo') ?>
 => <textarea name="foo">Beispieltext</textarea>

// Url-Gruppe
<?php echo link_to('klick mich', 'modul/aktion') ?>
=> <a href="/pfad/zur/aktion">klick mich</a>  // hängt von den Routing-Einstellungen ab

// Asset-Gruppe
<?php echo image_tag('bild', 'alt=foo size=200x100') ?>
 => <img src="/images/bild.png" alt="foo" width="200" height="100"/>
<?php echo javascript_include_tag('meinscript') ?>
 => <script language="JavaScript" type="text/javascript" src="/js/meinscript.js"></script>
<?php echo stylesheet_tag('style') ?>
 => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />

Es gibt viele weitere Helfer in Symfony, und es bräuchte eine ganzes Buch, um sie alle zu beschreiben. Die beste Referenz für die Helfer ist die Online-API-Dokumentation (http:// www.symfony-project.org/api/symfony.html), wo für alle Helfer ausführlich Syntax, Optionen und Beispiele dokumentiert sind.

Eigene Helfer hinzufügen

Symfony bringt viele Helfer für verschiedenste Zwecke mit, aber wenn Sie in der API-Dokumentation nicht das finden, was Sie benötigen, wollen Sie möglicherweise einen neuen Helfer erstellen. Das ist wirklich sehr einfach.

Helferfunktionen (normale PHP-Funktionen, die HTML-Code zurückgeben) sollten in einer Datei FoobarHelper.php gespeichert werden, wobei Foobar der Name der Helfergruppe ist. Speichern Sie die Datei im apps/frontend/lib/helper/-Verzeichnis (oder in irgendeinem helper/-Verzeichnis erstellt in einem der lib/-Verzeichnisse Ihres Projekts), und es kann automatisch mit use_helper('Foobar') eingebunden werden.

TIPP Das System erlaubt Ihnen sogar, die existierenden Symfony-Helfer zu überschreiben. Um etwa alle Helfer aus der Helfergruppe Text neu zu definieren, erzeugen Sie einfach eine Datei TextHelper.php in Ihrem apps/frontend/lib/helper/-Verzeichnis. Jedesmal, wenn Sie use_helper('Text') aufrufen, wird Symfony Ihre Text-Helfergruppe statt seine eigene benutzen. Aber Vorsicht: Da die Originaldatei gar nicht mehr beachtet wird, müssen Sie alle Funktionen der Helfergruppe neu definieren; andernfalls werden einige der Originalhelfer nicht mehr verfügbar sein.

Seitenlayout

Das Template in Codeabschnitt 7-1 ist kein gültiges XHTML-Dokument. Die DOCTYPE-Definition und die <html>- und <body>-Tags fehlen. Das liegt darain, dass sie anderswo in der Applikation gespeichert sind, in einer Datei layout.php, die das Seitenlayout enthält. Diese Datei, das globale Template, speichert den HTML-Code, der allen Seiten der Applikation gemeinsam ist, um zu verhindern, dass er in jedem Template wiederholt werden muss. Der Inhalt eines Templates wird ins Layout eingebettet oder, andersherum betrachtet, das Layout "dekoriert" das Template. Das ist eine Anwendung des Decorator-Design-Patterns, zu sehen in Abbildung 7-1.

TIPP Für weitere Informationen über das Decorator- und anderen Design-Pattern, siehe Patterns of Enterprise Application Architecture von Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0).

Abbildung 7-1 - Ein Template mit dem Layout dekorieren

[[Image(http://www.symfony-project.com/images/book/trunk/F0701.png)]]

Codeabschnitt 7-5 zeigt das Standard-Seitenlayout, zu finden im templates/-Verzeichnis der Applikation.

Codeabschnitt 7-5 - Standardlayout in meinprojekt/apps/frontend/templates/layout.php

[php]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
<?php echo include_http_metas() ?>
<?php echo include_metas() ?>
<?php echo include_title() ?>
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>

<?php echo $sf_data->getRaw('sf_content') ?>

</body>
</html>

Die Helfer im <head>-Bereich holsen sich Informationen aus dem Response-Objekt und der View-Konfiguration. Der <body>-Tag zeigt das fertige Template an. Mit diesem Layout, der Standardkonfiguration, und dem Beispieltemplate aus Codeabschnitt 7-1, sieht das Ganze am Schluss dann so aus wie in Codeabschnitt 7-6.

Codeabschnitt 7-6 - Das Layout, die View-Konfiguration und das Template

[php]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de" lang="de">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <meta name="title" content="Symfony-Projekt" />
  <meta name="robots" content="index, follow" />
  <meta name="description" content="Symfony-Projekt" />
  <meta name="keywords" content="symfony, projekt" />
  <title>Symfony-Projekt</title>
  <link rel="stylesheet" type="text/css" href="/css/main.css" />
  <link rel="shortcut icon" href="/favicon.ico">
</head>
<body>

<h1>Willkommen</h1>
<p>Willkommen zurück, <?php echo $name ?>!</p>
<ul>Was möchten Sie tun?
  <li><?php echo link_to('Die neusten Artikel lesen', 'article/read') ?></li>
  <li><?php echo link_to('Einen neuen Artikel schreiben', 'article/write') ?></li>
</ul>

</body>
</html>

Das globale Template kann vollständig an Ihre Applikation angepasst werden. Fügen Sie beliebigen HTML-Code hinzu. Dieses Layout wird oft dazu benutzt, die Navigation, das Logo usw. darzustellen. Sie können sogar mehrere Layouts haben und entscheiden, welches Layout für welche Aktion benutzt werden soll. Machen Sie sich um die Einbindung von JavaScript und CSS-Dateien keine Sorgen; der Abschnitt "View-Konfiguration" später in diesem Kapitel erklärt diese ausführlich.

Template-Shortcuts

In den Templates sind einige Symfony-Variablen immer verfügbar. Diese Shortcuts ermöglichen den Zugriff auf die wichtigsten Informationen über die Symfony-Kern-Objekte:

  • $sf_context: Das Context-Object (Instanz von sfContext)
  • $sf_request: Das Request-Objekt (Instanz von sfRequest)
  • $sf_params: Parameter des Requests
  • $sf_user: Das aktuelle User-Session-Objekt (Instanz von sfUser)

Das vorherige Kapitel hat hilfreiche Methoden des sfRequest- und des sfUser-Objekts beschrieben. Sie können diese Methoden in Templates durch die $sf_request- und $sf_user-Variablen aufrufen. Wenn zum Beispiel der Request einen total-Parameter enthält, ist sein Wert im Template folgendermaßen verfügbar:

[php]
// lange Version
<?php echo $sf_request->getParameter('total'); ?>

// kürzere Version
<?php echo $sf_params->get('total'); ?>

// gleichwertig mit dem folgenden Code in einer Aktion
<?php echo $this->getRequestParameter('total'); ?>

Codefragmente

Oft müssen Sie HTML- oder PHP-Code in mehrere Seiten einbinden. Die PHP-include()-Funktion hilft Ihnen dabei, dass Sie den Code nicht jedesmal wiederholen müssen.

Wenn zum Beispiel viele Templates Ihrer Applikation das gleiche Codefragment benötigen, speichern Sie es in einer Datei meinFragment.php im globalen Template-Verzeichns (meinprojekt/apps/frontend/templates/) und binden es in Ihr Template wie folgt ein:

[php]
<?php include(sfConfig::get('sf_app_template_dir').'/meinFragment.php') ?>

Aber das ist keine sehr saubere Art, ein Fragment zu verpacken, vor allem deshalb, weil sie verschiedene Variablennamen im Fragment und den verschiedenen Templates haben können, die es einbinden. Außerdem hat das Symfony-Cachesystem (näheres dazu in Kapitel 12) keine Möglichkeit, includes zu erkennen, das Codefragment kann also nicht uanbhängig vom Template im Cache abgelegt werden. Symfony bietet drei Alternativen zu gewöhnlichen includes:

  • Wenn die Logik simpel ist, können Sie einfach eine Templatedatei nehmen, der Sie einige Daten übergeben. Dazu verwenden Sie ein Partial.
  • Wenn die Logik komlizierter ist (zum Beispiel wenn sie aufs Datenmodell zugreift oder den Inhalt entsprechend der Session ändert), sollten Sie die Präsentation von der Logik trennen. Dazu benötigen Sie eine Component.
  • Wenn das Fragment einen bestimmten Teil des Layouts ersetzen soll, für den möglicherweise bereits Standardinhalt definiert wurde, brauchen Sie ein Slot.

NOTE Ein weiterer Codefragment-Typ, ein Component Slot, wird benutzt, wenn der Inhalt des Fragments vom Kontext abhäng (z.B. wenn das Fragment für verschiedene Aktionen eines Moduls unterschiedlichen Inhalt hat). Component Slots werden später in diesem Kapitel noch genauer beschrieben.

Die Einbindung dieser Fragmente geschieht durch Helfer der Partial-Gruppe. Diese Helfer sind in jedem Symfony-Template ohne Deklaration verfügbar.

Partials

Ein Partial ist ein wiederbenutzbares Stück Template-Code. In einer Publikationsapplikation wird der Templatecode zum Anzeigen eines Artikels sowohl in der Artikel-Detailseite verwendet als auch in der Liste der besten und derjenigen der neusten Artikel. Solcher Code ist ein perfekter Kandidat für ein Partial, wie in Abbildung 7-2 dargestellt.

Abbildung 7-2 - Partials in Templates wiederverwenden

[[Image(http://www.symfony-project.com/images/book/trunk/F0702.png)]]

Genau wie Templates sind Partials Dateien im templates/-Verzeichnis, die HTML-Code mit etwas PHP enthalten. Der Dateiname eines Partials beginnt immer mit einem Unterstrich (_), um sie gut von Templates unterscheiden zu können, die ja in den gleichen templates/-Verzeichnissen gespeichert sind.

Ein Template kann Partials einbinden, egal, ob sie aus dem gleichen Modul stammen, einem anderen oder aus dem globalen templates/-Verzeichnis. Sie können ein Partial mit dem include_partial()-Helfer einbinden und dabei das Modul und den Partialnamen wie in Codeabschnitt 7-7 gezeigt als Parameter angeben (ohne den führenden Unterstrich und die Dateiendung .php).

Codeabschnitt 7-7 - Ein Partial in ein Template des meinmodul-Moduls einbinden

[php]
// Binde das Partial frontend/modules/meinmodul/templates/_meinpartial1.php ein
// Da das Template und das Partial im gleichen Modul sind,
// können Sie den Modulnamen weglassen
<?php include_partial('meinpartial1') ?>

// Binde das Partial frontend/modules/foobar/templates/_meinpartial2.php ein
// Hier muss der Modulname angegeben werden
<?php include_partial('foobar/meinpartial2') ?>

// Binde das Partial frontend/templates/_meinpartial3.php ein
// Es wird als Teil des 'global'-Moduls betrachtet
<?php include_partial('global/meinpartial3') ?>

Partials haben Zugriff auf die üblichen Symfony-Helfer und Template-Shortcuts. Aber weil Partials überall in der Applikation aufgerufen werden können, haben sie nicht automatisch Zugriff auf die Variablen der Aktion, die das Template aufruft, in dem sie sich befinden - außer, wenn die Variablen explizit als Argument übergeben werden. Wenn etwa ein Partial Zugriff auf eine Variable $total haben soll, muss die Aktion diese dem Template übergeben und das Template sie dann dem Helfer als zweites Argument des include_partial()-Aufrufs, siehe Codeabschnitte 7-8, 7-9 und 7-10.

Codeabschnitt 7-8 - Die Aktion definiert eine Variable in meinmodul/actions/actions.class.php

[php]
class meinmodulActions extends sfActions
{
  public function executeIndex()
  {
    $this->total = 100;
  }
}

Codeabschnitt 7-9 - Das Template übergibt die Variable dem Partial, in meinmodul/templates/indexSuccess.php

[php]
<p>Hallo Welt!</p>
<?php include_partial('meinpartial',
array('meintotal' => $total)
) ?>

Codeabschnitt 7-10 - Das Partial kann die Variable jetzt verwenden, in meinmodul/templates/_meinpartial.php

[php]
<p>Total: <?php echo $meintotal ?></p>

TIPP Bisher wurden alle Helfer mit <?php echo functionName() ?> aufgerufen. Der Partial-Helper hingegen wird einfach nur mit <?php include_partial() ?> aufgerufen - ohne echo, so verhält es sich ähnlich dem normalen PHP-include()-Befehl. Sollten Sie jemals eine Funktion benötigen, die den Inhalt eines Partials zurückgibt ohne ihn anzuzeigen, verwenden Sie get_partial(). Alle in diesem Kapitel beschriebenen include_-Helfer haben ein get_-Pendant, das zusammen mit einem echo verwendet werden kann.

TIPP Neu in Symfony 1.1: Anstelle eines Templates kann eine Aktion auch ein Partial zurückgeben. Die Methode renderPartial() in der Aktion fördert die Wiederverwendbarkeit von Code und nutzt die Cache-Fähigkeiten der Partials (siehe Kapitel 12). Die Variablen, die in der Aktion definiert sind, werden dem Partial automatisch übergeben.

[php]
public function executeIndex()
{
  // Dinge tun
  $this->mytotal = 1234;

  return $this->renderPartial('meinmodul/meinpartial');
}

Komponenten

Das erste Beispielskript in Kapitel 2 war in zwei Teile aufgeteilt, um die Logik von der Präsentation zu trennen. Genau wie das MVC-Pattern auf Aktionen und Templates zutrifft, müssen Sie möglicherweise ein Partial in einen Logik- und einen Präsentationsteil aufteilen. In so einem Fall sollten sie eine Komponente benutzen.

Eine Komponente ist wie eine Aktion, nur viel schneller. Die Logik einer Komponente befindet sich in einer Klasse, die von sfComponents erbt, sie befindet sich in der Datei action/components.class.php. Die Präsentation befindet sich in einem Partial. Methoden der sfComponents-Klasse beginnen mit execute, genau wie Aktionen, und genau wie Aktionen können sie Variablen an den Präsentationsteil übergeben. Partials, die zusammen mit einer Komponente verwendet werden, werden nach dieser benannt (ohne das führende execute, aber dafür mit einem Unterstrich). Tabelle 7-1 vergleich die Namenskonventionen von Aktionen und Komponenten.

Tabelle 7-1 - Aktions- und Komponenten-Namenskonventionen

Konvention | Aktionen | Komponenten --------------------- | ------------------------ | -------------------------- Logikdatei | actions.class.php | components.class.php Logikklasse erweitert | sfActions | sfComponents Methodennamen | executeMeineAktion() | executeMeineKomponente() Präsentationsdatei | meineAktionSuccess.php | _meineKomponente.php

TIPP Genau wie Sie Sie Aktionsdateien aufspalten können, hat die sfComponents-Klasse ein sfComponent-Pendant, das einzelne Komponentendateien mit der gleichen Syntax erlaubt.

Ein Beispiel: Nehmen wir an, Sie haben eine Sidebar, in der Sie die neusten Nachrichten eines bestimmten Themas anzeigen, abhängig vom Benutzerprofil; diese Sidebar wird auf verschiedenen Seiten verwendet. Die benötigten Querys, um die Headlines zu holen, sind zu kompliziert für ein einzelnes Partial, also müssen sie in eine aktionsartige Datei verschoben werden – eine Komponente. Abbildung 7-3 illustriert dieses Beispiel.

Für dieses Beispiel (siehe Codeabschnitte 7-11 und 7-12) befindet sich die Komponente in ihrem eigenen Modul news, aber Sie können Komponenten und Aktionen auch in einem einzigen Modul mischen, wenn es Sinn macht.

Abbildung 7-3 - Komponenten in Templates verwenden

[[Image(http://www.symfony-project.com/images/book/trunk/F0703.png)]]

Codeabschnitt 7-11 - Die Komponentenklasse in modules/news/actions/components.class.php

[php]
<?php

class newsKomponenten extends sfComponents
{
  public function executeHeadlines()
  {
    $c = new Criteria();
    $c->addDescendingOrderByColumn(NewsPeer::EINGETRAGEN_AM);
    $c->setLimit(5);
    $this->news = NewsPeer::doSelect($c);
  }
}

Codeabschnitt 7-12 - Das Partial in modules/news/templates/_headlines.php

[php]
<div>
  <h1>Neuste Nachrichten</h1>
  <ul>
  <?php foreach($news as $headline): ?>
    <li>
      <?php echo $headline->getEingetragenAm() ?>
      <?php echo link_to($headline->getTitel(),'news/show?id='.$headline->getId()) ?>
    </li>
  <?php endforeach ?>
  </ul>
</div>

Jetzt können Sie jedesmal, wenn Sie die Komponente in einem Template benötigen, einfach diesen Code einbinden:

[php]
<?php include_component('news', 'headlines') ?>

Genau wie Partials akzeptieren Komponenten als zusätzliche Parameter assoziative Arrays. Die Parameter sind in den Partials unter ihrem eigenen Namen verfügbar, und in Komponenten über das $this-Objekt (siehe Codeabschnitt 7-13).

Codeabschnitt 7-13 - Parameter an eine Komponente und das dazugehörige Template übergeben

[php]
// Komponente einbinden
<?php include_component('news', 'headlines', array('foo' => 'bar')) ?>

// In der Komponente selbst
echo $this->foo;
 => 'bar'

// Im _headlines.php-Partial
echo $foo;
 => 'bar'

Sie können sogar Komponenten in andere Komponenten einbinden, oder auch im globalen Layout, wie jedes normale Template. Wie Aktionen führen Komponenten Methoden aus, die Variablen an das zugehörige Partial übergeben und Zugriff auf die gleichen Shortcuts haben. Aber die Ähnlichkeiten enden hier. Eine Komponente kümmert sich nicht um Sicherheit oder Validation, kann nicht vom Internet aus aufgerufen werden (nur von der Applikation selbst) und hat nicht mehrere Rückgabemöglichkeiten. Darum ist die Ausführung einer Komponente schneller als die einer Aktion.

Slots

Partials und Komponenten dienen der Wiederbenutzbarkeit. Aber oft sollen Codefragmente ein Layout mit verschiedenen dynamischen Bereichen ausfüllen. Beispielsweise möchten Sie einige individuelle Tags in den <head>-Bereich Ihres Layouts einbinden, abhängig von der aufgerufenen Aktion. Oder die Aktion füllt verschiedene größere dynamische Bereiche auf der Seite und zusätzlich viele kleinere, für die ein Standardinhalt im Layout festgelegt ist, der aber von Templates überschrieben werden kann.

In solchen Situationen ist die Lösung ein Slot. Ein Slot ist ein Platzhalter, den man in einem beliebigen View-Element einsetzen kann. (Layout, Template, Partial) Diesen Platzhalter ausfüllen ist genau wie eine Variable setzen. Der Code, mit dem der Slot gefüllt wird, wird im Response-Objekt gespeichert, Sie können es also in jedem View-Element definieren. Nur sollten Sie sicherstellen, dass ein Slot definiert ist, bevor er eingebunden wird; und denken Sie daran: Das Layout wird nach dem Template gerendert (das ist der Dekorationsprozess), und die Partials werden dann gerendert, wenn sie von einem Template aus aufgerufen werden. Klingt zu kompliziert? Sehen wir uns ein Beispiel an.

Nehmen wir ein Layout mit einem Bereich fürs Template und zwei Slots: Einen für die Sidebar und den anderen für den Footer. Die Slotwerte werden in den Templates definiert. Während dem Dekorationsprozesses enthält das Layout den Templatecode, und die Slots werden mit den vorher definierten Werten gefüllt, wie in Abbildung 7-4 zu sehen. Die Sidebar und der Footer können dann abhängig von der Aktion geändert werden. Das ist so, als ob Sie ein Layout mit mehreren "Löchern" haben.

Abbildung 7-4 - Layoutslots in einem Template

[[Image(http://www.symfony-project.com/images/book/trunk/F0704.png)]]

Wenn wir uns etwas Code ansehen, wird die Sache schnell verständlicher. Um einen Slot einzubinden, verwenden Sie den include_slot()-Helfer. Der has_slot()-Helfer gibt true zurück, wenn der Slot bereits definiert wurde. Um einen Platzhalter für den Slot 'sidebar' im Layout mitsamt seinem Standardinhalt zu definieren, verwenden Sie den Code aus Codeabschnitt 7-14.

Codeabschnitt 7-14 - Den Slot 'sidebar' ins Layout einbinden

[php]
<div id="sidebar">
<?php if (has_slot('sidebar')): ?>
  <?php include_slot('sidebar') ?>
<?php else: ?>
  <!-- Standard-Sidebar-Inhalt -->
  <h1>Individuellelr Bereich</h1>
  <p>Dieser Bereich enthält Links und Informationen
  abhängig vom Hauptbereich der Seite.</p>
<?php endif; ?>
</div>

Jedes Template kann die Inhalt eines Slots definieren (sogar Partials können das). Da Slots dazu gedacht sind, HTML-Code zu beinhalten, bietet Symfony einen komfortablen Weg, sie zu definieren: Schreiben Sie den Slot-Code einfach zwischen einen Aufruf des slot()- und des end_slot()-Helfer, so wie in Codeabschnitt 7-15.

Codeabschnitt 7-15 - Den Inhalt von Slot 'sidebar' in einem Template überschreiben

[php]
...
<?php slot('sidebar') ?>
  <!-- Individueller Sidebar-Inhalt für das aktuelle Template -->
  <h1>Benutzerinformationen</h1>
  <p>Name:  <?php echo $user->getName() ?></p>
  <p>E-Mail-Adresse: <?php echo $user->getEmail() ?></p>
<?php end_slot() ?>

Der Code zwischen den Slothelfern wird im Kontext des Templates ausgeführt, es hat also keinen Zugriff auf die Variablen der Aktion. Symfony fügt diesen Code automatisch dem Response-Objekt hinzu. Es wird nicht im Template angezeigt, aber ist für künftige include_slot()-Aufrufe verfügbar, wie in Codeabschnitt 7-14 zu sehen.

Slots sind sehr nützlich für Bereiche mit individuellem Inhalt. Sie können auch benutzt werden, wenn HTML-Code nur von gewissen Aktionen angezeigt werden soll. Zum Beispiel hat es in einem Template, das die Liste der neusten Nachrichten anzeigt, im <head>-Bereich des Layouts einen Link zu einem RSS-Feed. Das kann man mit einem 'feed'-Slot im Layout lösen, den man dann im Template der Liste setzt.

SIDEBAR Wo die Templatefragmente zu finden sind

Leute, die mit Templates arbeiten, sind normalerweise Webdesigner, die Symfony möglicherweise nicht gut kennen und Schiwerigkeiten haben, Templatefragmente zu finden, da diese überall in der Applikation verteilt sein können. Diese Richtlinien wird ihnen die Arbeit mit dem Symfony-Templatingsystem erleichtern.

Obwohl ein Symfony-Projekt viele Verzeichnisse enthält, befinden sich alle Layouts, Templates und Templatefragmente in Verzeichnissen mit dem Namen templates/. Was also den Webdesigner angeht, kann eine Projektstruktur auf das folgende vereinfacht werden:

meinproject/ apps/ applikation1/ templates/ # Layouts für Applikation 1 modules/ modul1/ templates/ # Templates und Partials für Modul 1 modul2/ templates/ # Templates und Partials für Modul 2 modul3/ templates/ # Templates und Partials für Modul 3

Alle anderen Verzeichnisse können ignoriert werden.

Wenn sie auf ein include_partial() treffen, müssen Webdesigner lediglich verstehen, dass nur das erste Argument wichtig ist. Dieses Argument ist immer von der Form modul_name/partial_name, und das heißt, dass sich der Präsentationscode in modules/modul_name/templates/_partial_name.php befindet.

Beim include_component()-Helfer ist es so, dass Modulname und Partialname die ersten beiden Argumente sind. Für den Rest genügt es eigentlich, eine grundsätzliche Idee davon zu haben, was Helfer sind und welche Helfer am häufigsten in Templates verwendet werden.

View-Konfiguration

In Symfony besteht eine View aus zwei unabhängigen Teilen:

  • Die HTML-Darstellung des Ergebnisses einer Aktion (gespeichert in einem Template, im Layout und in den verschiedenen Templatefragmenten)

  • Der ganze Rest, darunter folgendes:

    • Metadeklarationen: Stichwörter, Beschreibung, Cache-Dauer usw.
    • Seitentitel: Hilft nicht nur beim Tabbed Browsing, Ihre Seite zu finden, sondern ist auch für Suchmaschinen sehr wichtig.
    • Dateieinbindungen: JavaScripts und CSS-Dateien
    • Layout: Gewisse Aktionen benötigen ein individuelles Layout (Pop-Ups, Werbungen usw.) oder überhaupt kein Layout (z.B. Ajax-Aktionen).

In der View wird alles, das nicht HTML ist, als View-Konfiguration bezeichnet, und Symfony bietet zwei Möglichkeiten, diese zu bearbeiten. Der normale Weg geht über die view.yml-Konfigurationsdatei. Sie kann immer dann benutzt werden, wenn Werte nicht vom Kontext oder von Datenbank-Querys abhängen. Wenn Sie jedoch dynamische Werte setzen müssen, sollten Sie die alternative Möglichkeit verwenden und die Konfiguration direkt über das sfResponse-Objekt in der Aktion selbst bearbeiten.

NOTE Wenn Sie einen View-Konfigurationsparameter sowohl über das sfResponse-Objekt wie auch über die view.yml setzen, hat der Wert im sfResponse-Objekt Vorrang.

Die view.yml-Datei

Jedes Modul kann eine view.yml-Datei haben, die die Einstellungen seiner Views definiert. So können Sie Vieweinstellungen für ein ganzes Modul in einer einzigen Datei verwalten. Die Schlüssel in der view.yml sind die Modul-View-Namen. Codeabschnitt 7-16 zeigt ein Beispiel einer Viewkonfiguration.

Codeabschnitt 7-16 - Beispielhafte view.yml

editSuccess:
  metas:
    title: Bearbeiten Sie ihr Profil

editError:
  metas:
    title: Fehler bei der Bearbeitung des Profils

all:
  stylesheets: [mein_style]
  metas:
    title: Meine Website

ACHTUNG Achten Sie darauf, dass die Hauptschlüssel in der view.yml Viewnamen sind und keine Aktionsnamen. Zur Erinnerung: Ein Viewname setzt sich zusammen aus einem Aktionsnamen und einem Aktions-Beende-Namen. Wenn die edit-Aktion sfView::SUCCESS zurückgibt (oder gar nichts, da dies die standardmäßige Rückgabe ist), dann ist der Viewname editSuccess.

Die Standardeinstellungen für ein Modul sind unter dem all:-Schlüssel in der Modul-view.yml definiert. Die Standardeinstellungen für alle Applilkationsviews befinden sich in der Applikations-view.yml. Wieder einmal erkennen Sie hier die Verschachtelung der Konfiguration:

  • In apps/frontend/modules/meinmodul/config/view.yml können Sie Definitionen für einzelne Views festlegen, die die modulweiten Definitionen überschreiben
  • In apps/frontend/modules/meinmodul/config/view.yml können Sie unter dem all:-Schlüssel Viewdefinitionen für alle Aktionen des Moduls festlegen, die die applikationsweiten Definitionen überschreiben.
  • In apps/frontend/config/view.yml können Sie applikationsweite Definitionen festlegen, die für alle Module und alle Aktionen der Applikation gelten.

TIPP Modulweite view.yml-Dateien existieren standardmäßig überhaupt nicht. Sie müssen also, wenn Sie das erste Mal eine Viewkonfiguration für ein bestimmtes Modul anpassen wollen, eine view.yml im config/-Verzeichnis dieses Moduls anlegen.

Nachdem Sie das Standardtemplate in Codeabschnitt 7-5 und ein Beispiel der fertigen Antwort in Codeabschnitt 7-6 gesehen haben, fragen Sie sich vielleicht, wo die Header-Werte herkommen. Die kommen schlicht und einfach aus den Standard-Vieweinstellungen, definiert in der Applikations-view.yml, zu sehen in Codeabschnitt 7-17.

Codeabschnitt 7-17 - Standard-Applikations-Viewkonfiguration in apps/frontend/config/view.yml

default:
  http_metas:
    content-type: text/html

  metas:
    title:        Symfony-Projekt
    robots:       index, follow
    description:  Symfony-Projekt
    keywords:     symfony, projekt
    language:     de

  stylesheets:    [main]

  javascripts:    [ ]

  has_layout:     on
  layout:         layout

Jede dieser Einstellungen wird im Abschnitt "View-Konfigurationseinstellungen" deatilliert beschrieben.

Das Response-Objekt

Obwohl es eigentlich Bestandteil der View-Ebene ist, wird das Response-Objekt oft von der Aktion selbst verändert. Aktionen können auf das Symfony-Response-Objekt, sfResponse, mittels der getResponse()-Methode zugreifen. Codeabschnitt 7-18 zeigt einige der häufig verwendeten sfResponse-Methoden.

Codeabschnitt 7-18 - Aktionen haben Zugriff auf die sfResponse-Objektmethoden

[php]
class meinmodulActions extends sfActions
{
  public function executeIndex()
  {
    $response = $this->getResponse();

    // HTTP-Header
    $response->setContentType('text/xml');
    $response->setHttpHeader('Content-Language', 'de');
    $response->setStatusCode(403);
    $response->addVaryHttpHeader('Accept-Language');
    $response->addCacheControlHttpHeader('no-cache');

    // Cookies
    $response->setCookie($name, $content, $expire, $path, $domain);

    // Metadaten und Titel
    $response->addMeta('robots', 'NONE');
    $response->addMeta('keywords', 'foo bar');
    $response->setTitle('Meine Seite');
    $response->addStyleSheet('custom_style');
    $response->addJavaScript('custom_behavior');
  }
}

Zusätzlich zu den setter-Methoden, die Sie hier sehen können, hat die sfResponse-Klasse auch getter, die den aktuellen Wert eines Response-Attributes zurückgeben.

Die Header-Setter in Symfony sind sehr hilfreich. Header werden erst so spät wie möglich geschickt (im sfRenderingFilter), Sie können Sie also so oft und so stark ändern, wie Sie wollen. Die Setter bieten Ihnen auch sehr nützliche Shortcuts. Wenn Sie beispielsweise keinen Zeichensatz spezifizieren beim Aufruf von setContentType(), dann fügt Symfony automatisch den Standard-Zeichensatz aus der settings.yml hinzu.

[php]
$response->setContentType('text/xml');
echo $response->getContentType();
 => 'text/xml; charset=utf-8'

Der Statuscode von Antworten in Symfony entspricht der HTTP-Spezifikation. Exceptions liefern einen Status 500, nicht gefundene Seiten 404, normale Seiten 200, nicht veränderte Seiten können Status 304 zurückgeben (siehe Kapitel 12 für Details) usw. Aber Sie können diese Standards überschreiben, indem Sie Ihren eigenen Statuscode in der Aktion mit der setStatusCode()-Response-Methode definieren. Sie können Ihren eigenen Code und Ihre eigene Nachricht definieren, oder auch nur Ihren eigenen Code -- in diesem Fall wird Symfony die passende Nachricht zu diesem Code automatisch hinzufügen.

[php]
$response->setStatusCode(404, 'Diese Seite existiert nicht mehr');

TIPP Bevor die Header geschickt werden, normalisiert Symfony ihre Namen. Sie müssen sich also nicht darum kümmern, ob Sie in einem setHttpHeader()-Aufruf content-language anstatt Content-Language schreiben, Symfony versteht auch ersteres und wandelt es automatisch in die korrekte Schreibweise um.

View-Konfigurationseinstellungen

Vielleicht haben Sie bemerkt, dass es zwei Arten von View-Konfigurationseinstellungen gibt:

  • Diejenigen, die einen eindeutigen Wert haben (der Wert ist ein String in der view.yml-Datei und das Response-Objekt benutzt eine set-Methode dafür)
  • Diejenigen mit mehreren Werten (für die die view.yml Arrays benutzt und das Response-Objekt eine add-Methode)

Denken Sie daran, dass eine Konfigurationenfolge die eindeutigen Werte löscht, sie aber zu den Einstellungen mit mehreren Werten hinzufügt. Das wird im Verlauf dieses Kapitels noch klarer werden.

Meta-Tag-Konfiguration

Die Informationen in den <meta>-Tags der Antwort werden im Browser nicht angezeigt, sind aber nützlich für Robots und Suchmaschinen. Sie kontrollieren auch das Cache-Verhalten einer Seite. Definieren Sie diese Tags unter den http_metas:- und metas:-Schlüsseln in der view.yml wie in Codeabschnitt 7-19 oder mit den addHttpMeta()- und addMeta()-Antwortmethoden in der Aktion wie in Codeabschnitt 7-20.

Codeabschnitt 7-20 - Metadefinitionen als Schlüssel-Wert-Paare in der view.yml

http_metas:
  cache-control: public

metas:
  description:   Finanzen in Frankreich
  keywords:      finanzen, frankreich

Codeabschnitt 7-20 - Metadefinitionen als Antworteinstellungen in der Aktion

[php]
$this->getResponse()->addHttpMeta('cache-control', 'public');
$this->getResponse()->addMeta('description', 'Finanzen in Frankreich');
$this->getResponse()->addMeta('keywords', 'finanzen, frankreich');

Einen existierenden Schlüssel hinzufügen ersetzt standardmäßig seinen aktuellen Inhalt. Für HTTP-Meta-Tags können Sie einen dritten Parameter übergeben – ist dieser auf false gesetzt, hängt die addHttpMeta()-Methode (genau wie die setHttpHeader()-Methode) den Wert an den existierenden an, statt ihn zu ersetzen.

[php]
$this->getResponse()->addHttpMeta('accept-language', 'en');
$this->getResponse()->addHttpMeta('accept-language', 'fr', false);
echo $this->getResponse()->getHttpHeader('accept-language');
 => 'en, fr'

Damit diese Metatags im fertigen Dokument erscheinen, müssen der include_http_metas()- und der include_metas()-Helfer im <head>-Bereich aufgerufen werden. (Das wird im Standardlayout automatisch gemacht; siehe Codeabschnitt 7-5.) Symfony sammelt automatisch die Einstellungen aus allen view.yml-Dateien (inklusive derjenigen in Codeabschnitt 7-17) und wandelt sie in meta-Tags um. Das Beispiel aus Codeabschnitt 7-19 verwandelt sich also in Codeabschnitt 7-21.

Codeabschnitt 7-21 - Ausgegebene Metatags auf der fertigen Seite

[php]
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="cache-control" content="public" />
<meta name="robots" content="index, follow" />
<meta name="description" content="Finanzen in Frankreich" />
<meta name="keywords" content="finanzen, frankreich" />

Zusätzlich wird der HTTP-Header der Antwort von der http-metas:-Definition beeinflusst, selbst wenn Sie gar keine include_http_metas()-Helfer in Ihrem Layout haben, oder wenn Sie überhaupt kein Layout haben – zum Beispiel, wenn Sie eine Datein als Plaintext senden müssen. Dann definieren Sie einfach folgende view.yml:

http_metas:
  content-type: text/plain

has_layout: false

Titelkonfiguration

Der Seitentitel ist ein integraler Bestandteil der Suchmaschinenoptimierung. Auch bei modernen, tabbed-browsing-fähigen Browsern trägt der zum Überblick bei. Im HTML-Code ist der Titel sowohl ein Tag als auch Metainformation über die Seite, deshalb ist in der view.yml der title:-Schlüssel ein Kind des metas:-Schlüssels. Codeabschnitt 7-22 zeigt die Titeldefinition in der view.yml, und Codeabschnitt 7-23 zeigt die Definition in einer Aktion.

Codeabschnitt 7-22 - Titeldefinition in der view.yml

indexSuccess:
  metas:
    title: Drei kleine Ferkel

Codeabschnitt 7-23 - Titeldefinition in der Aktion – ermöglicht dynamische Titel

[php]
$this->getResponse()->setTitle(sprintf('%d kleine Ferkel', $anzahl));

Im <head>-Bereich der fertigen Seite erzeugt die Titeldefinition einen <meta name="title">-Tag, wenn der include_metas()-Helfer verwendet wird, und einen <title>-Tag, wenn der include_title()-Helfer verwendet wird. Wenn beide verwendet werden (was im Standardlayout der Fall ist, s. Codeabschnitt 7-5), erscheint der Titel zweimal im Quelltext (siehe Codeabschnitt 7-6).

Konfigurieren der einzubindenden Dateien

Eine CSS- oder JavaScript-Datei zu einer View hinzufügen ist sehr einfach, wie in Codeabschnitt 7-24 zu sehen.

Codeabschnitt 7-24 - Dateieinbindung

// In der view.yml
indexSuccess:
  stylesheets: [meinstyle1, meinstyle2]
  javascripts: [meinscript]

[php]
// In der Aktion
$this->getResponse()->addStylesheet('meinstyle1');
$this->getResponse()->addStylesheet('meinstyle2');
$this->getResponse()->addJavascript('meinscript');

// Im Template
<?php use_stylesheet('mystyle1') ?>
<?php use_stylesheet('mystyle2') ?>
<?php use_javascript('myscript') ?>

In jedem Fall ist das Argument ein Dateiname. Wenn die Datei eine logische Endung hat (.css für ein Stylesheet und .js für ein JavaScript), können Sie es auch weglassen. Wenn die Datei in einem logischen Verzeichnis liegt (/css/ für ein Stylesheet, /js/ für ein JavaScript), können Sie das ebenfalls weglassen. Symfony ist so schlau und findet die korrekte Dateiendung und das korrekte Verzeichnis selbst heraus.

Anders als die Meta- und Titel-Definitionen benötigen die Datei-Einbindungen keine Helfer im Template oder Layout. Das heißt, die obigen Einstellungen geben den HTML-Code aus Codeabschnittt 7-25 zurück, egal, was im Template und/Oder im Layout steht.

Codeabschnitt 7-25 - Datei-Einbindung – Im Layout wird kein Helfer benötigt

[php]
<head>
...
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle1.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/mystyle2.css" />
<script language="javascript" type="text/javascript" src="/js/myscript.js">
</script>
</head>

NOTE Die Einbindung von CSS und JavaScript in der Antwort wird vom Filter sfCommonFilter übernommen. Dieser sucht in der Antwort nach einem <head>-Tag und fügt die <link>s und <script>s direkt vor dem schließenden </head> ein. Dies bedeutet jedoch auch, dass die Einbindung nicht funktioniert, wenn in Ihrem Layout oder Template kein <head>-Tag vorhanden ist.

Denken Sie daran: Auch hier findet die Konfigurationskaskade wieder Anwendung: Jede Datei, die in der Applikations-view.yml definiert wurde, wird in jeder Seite der Applikation eingebunden. Die Codeabschnitte 7-26, 7-27 und 7-28 demonstrieren dies.

Codeabschnitt 7-26 - Beispiel-Applikations-view.yml

default:
  stylesheets: [main]

Codeabschnitt 7-27 - Beispiel-Modul-view.yml

indexSuccess:
  stylesheets: [special]

all:
  stylesheets: [additional]

Codeabschnitt 7-28 - Der View indexSuccess als Ergebnis

[php]
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/additional.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/special.css" />

Wenn Sie eine Datei entfernen möchten, die auf einer höheren Stufe definiert wurde, stellen Sie einfach ein Minuszeichen (-) vor den Dateinamen, wie in Codeabschnitt 7-29.

Codeabschnitt 7-29 - Beispiel-Modul-view.yml, die eine Datei entfernt, die auf der Applikationsstufe definiert wurde

indexSuccess:
  stylesheets: [-main, special]

all:
  stylesheets: [additional]

Um alle CSS- oder JavaScript-Dateien zu entfernen, verwenden Sie -* als Dateinamen, wie in Codeabschnitt 7-30.

Codeabschnitt 7-30 - Beispiel-Modul-view.yml, die alle Dateien entfernt, die auf der Applikationsstufe definiert wurden

indexSuccess:
  stylesheets: [-*]
  javascripts: [-*]

Sie können auch weiter ins Detail gehen und in einem zusätzlichen Parameter angeben, wo genau die Datei eingebunden werden soll (an der ersten oder letzten Stelle), wie in Codeabschnitt 7-31. Dies funktioniert sowohl für Stylesheets als auch für Javascript-Dateien.

Codeabschnitt 7-31 - Angabe der Position der einzubindenden Datei

// In der view.yml
indexSuccess:
  stylesheets: [special: { position: first }]

[php]
// In der Aktion
$this->getResponse()->addStylesheet('special', 'first');

// Im Template
<?php use_stylesheet('special', 'first') ?>

Neu in Symfony 1.1: Sie können sich auch dazu entschließen, die Transformation des einzubindenden Dateinamens zu umgehen, so dass die resultierenden <link>- bzw. <script>-Tags direkt auf den übergebenen Dateiort verweisen, wie in Codeabschnitt 7-32 gezeigt.

Codeabschnitt 7-32 - CSS-Einbindung mit nicht-transformiertem Dateinamen

// In der view.yml
indexSuccess:
  stylesheets: [main, paper: { raw_name: true }]

[php]
// In der Aktion
$this->getResponse()->addStylesheet('paper', '', array('raw_name' => true));

// Im Template
<?php use_stylesheet('paper', '', array('raw_name' => true)) ?>

// Resultierender View
<link rel="stylesheet" type="text/css" media="print" href="paper" />

Um Medientypen für CSS-Dateien hinzuzufügen, können Sie die Standard-CSS-Tag-Optionen ändern, zu sehen in den Codeabschnitten 7-33.

Codeabschnitt 7-33 - CSS-Einbindung mit Medientyp

// In der view.yml
indexSuccess:
  stylesheets: [main, paper: { media: print }]

[php]
// In der Aktion
$this->getResponse()->addStylesheet('paper', '', array('media' => 'print'));

// Im Template
<?php use_stylesheet('paper', '', array('media' => 'print')) ?>

// Resultierender View
<link rel="stylesheet" type="text/css" media="print" href="/css/paper.css" />

Layout-Konfiguration

Abhängig vom gestalterischen Aufbau Ihrer Website haben Sie möglicherweise mehrere Layouts. Klassische Websites haben mindestens zwei: Das Standardlayout und das Pop-up-Layout.

Sie wissen bereits, dass das Standardlayout sich in frontend/apps/meineapp/templates/layout.php befindet. Zusätzliche Layouts müssen im gleichen globalen templates/-Verzeichnis hinzugefügt werden. Wenn Sie möchten, dass eine View die Datei meineapp/templates/mein_layout.php verwendet, benutzen Sie die Syntax aus Codeabschnitt 7-34.

Codeabschnitt 7-34 - Layoutdefinition

// In der view.yml
indexSuccess:
  layout: my_layout

[php]
// In der Aktion
$this->setLayout('my_layout');

// Im Template
<?php decorate_with('my_layout') ?>

Einige Views benötigen überhaupt kein Layout (z.B. Plain-Text-Seiten oder RSS-Feeds). In diesem Fall setzen Sie has_layout auf false, wie im Codeabschnitt 7-35.

Codeabschnitt 7-35 - Layout-Entfernung

// In der view.yml
indexSuccess:
  has_layout: false

[php]
// In der Aktion
$this->setLayout(false);

// Im Template
<?php decorate_with(false) ?>

NOTE Ajax-Aktions-Views haben standardmäßig kein Layout.

Komponentenslots

Um die Vielseitigkeit von Viewkomponenten und Viewkonfiguration zu vereinigen, gibt es das Komponentenslot-System. Es ist eine Alternative zu Slots, speziell zugeschnitten auf Wiederverwendbarkeit und Layer-Trennung. Komponentenslots sind strukturierter aufgebaut als normale Slots, brauchen aber in der Ausführung etwas länger.

Genau wie Slots sind auch Komponentenslots benannte Platzhalter, die Sie in den View-Elementen definieren können. Der Unterschied besteht darin, wie der ersetzende Code bestimmt wird. Bei einem Slot befindet sich der Code in einem anderen View-Element; bei einem Komponentenslot ist der Code das Ergebnis der Ausführung einer Komponente, und der Name dieser Komponente kommt aus der Viewkonfiguration. Sie werden Komponentenslots besser verstehen, wenn Sie sie erst einmal in Aktion erlebt haben.

Um einen Komponentenslot-Platzhalter zu setzen, verwenden Sie den include_component_slot()-Helfer. Diese Funktion erwartet ein Label als Parameter. Nehmen Sie an, die layout.php Ihrer Applikation enthält eine kontextabhängige Sidebar. Codeabschnitt 7-36 zeigt, wie der Komponentslot-Helfer eingebunden wird.

Codeabschnitt 7-36 - Einen Komponentenslot mit dem Namen 'sidebar' einbinden

[php]
...
<div id="sidebar">
  <?php include_component_slot('sidebar') ?>
</div>

Definieren Sie die Verbindung von Komponentenslot-Labe und Komponentenname in der Viewkonfiguration. Setzen Sie die Standardkomponente für den Komponentenslot 'sidebar' in der Applikations-view.yml, unter dem components-Header. Der Schlüssel ist das Komponentenslot-Label; der Wert muss ein Array sein, der ein Modul und einen Komponentennamen beinhaltet. Ein Beispiel ist in Codeabschnitt 7-37 zu sehen.

Codeabschnitt 7-37 - Den Standard-'Sidebar'-Komponentenslot in der meineapp/config/view.yml definieren

default:
  components:
    sidebar:  [bar, default]

Wenn das Layout ausgeführt wird, wird der Komponentenslot 'sidebar' mit dem Ergebnis der executeDefault()-Methode der barComponents-Klasse innerhalb des bar-Moduls gefüllt, und diese Methode wird das _default.php-Partial anzeigen, das sich in modules/bar/templates/ befindet.

Die Konfigurationskaskade ermöglicht Ihnen, diese Einstellung für ein bestimmtes Modul zu überschreiben. In einem Benutzermodul möchten Sie vielleicht, dass in der Sidebar der Benutzername und die Anzahl Artikel stehen, die dieser Benutzer veröffentlicht hat. In diesem Fall können Sie die Sidebar-Slot-Einstellung in der Modul-view.yml ändern wie in Codeabschnitt 7-38.

Codeabschnitt 7-38 - Den Komponentenslot 'sidebar' spezialisieren in meineapp/modules/user/config/view.yml

all:
  components:
    sidebar:  [bar, user]

Die Komponentendefinitionen, die diesen Slot behandeln sollen, sehen so aus wie in Codeabschnitt 7-39.

Codeabschnitt 7-39 - Vom Slot 'sidebar' verwendete Komponenten in modules/bar/actions/components.class.php

[php]
class barComponents extends sfComponents
{
  public function executeDefault()
  {
  }

  public function executeUser()
  {
    $this->current_user = $this->getUser()->getCurrentUser();
    $c = new Criteria();
    $c->add(ArticlePeer::AUTHOR_ID, $this->current_user->getId());
    $this->nb_articles = ArticlePeer::doCount($c);
  }
}

Codeabschnitt 7-40 zeigt die Views für diese zwei Komponenten.

Codeabschnitt 7-40 - Von den Komponenten des Slots 'sidebar' verwendete Partials in modules/bar/templates/

[php]
// _default.php
<p>This zone contains contextual information.</p>

// _user.php
<p>User name: <?php echo $current_user->getName() ?></p>
<p><?php echo $nb_articles ?> articles published</p>

Komponentenslots können Sie unter anderem einsezten für: Breadcrumb-Navigationen, kontextsensitive Navigationen sowie dynamische Einbindungen aller Art. Sie können im globalen Layout und in Templates verwendet werden, und sogar in anderen Komponenten. Die Konfigurationseinstellung eines Komponentenslots ist immer diejenige der Konfiguration der letzten aufgerufenen Aktion.

Wenn der Komponentenslot für ein bestimmtes Modul leer bleiben soll, definieren Sie ihn einfach als leer, wie in Codeabschnitt 7-41.

Codeabschnitt 7-41 - Einen Komponentenslot in der view.yml deaktivieren

all:
  components:
    sidebar:  []

Output Escaping

Wenn Sie dynamische Daten in ein Template einfügen, müssen Sie die Datenintegrität sicherstellen. Wenn Daten aus einem Formular kommen, besteht die Gefahr, dass sie bösartige Scripte enthalten, die versuchen, Cross-Site-Scripting-Attacken (XSS) auszuführen. Sie müssen den Output maskieren (escapen), so dass allfälliger darin enthaltener HTML-Code wirkungslos wird.

Nehmen Sie an, ein Benutzer füllt ein Eingabefeld mit dem folgenden Wert:

[php]
<script>alert(document.cookie)</script>

Wenn Sie diesen Wert unvorsichtigerweise einfach ausgeben, wird das JavaScript in jedem Browser ausgeführt und erlaubt so noch wesentlich gefährlichere Angriffe als nur ein alert. Darum müssen Sie den Output maskieren, bevor Sie ihn anzeigen. Dann sieht er so aus:

[php]
&lt;script&gt;alert(document.cookie)&lt;/script&gt;

Sie könnten den Output manuell maskieren, indem Sie ihn jedesmal mit htmlentities() umschließen, aber das wäre sehr fehleranfällig. Symfony bietet stattdessen ein spezielles System, genannt Output Escaping, das automatisch jeden dynamischen Output in einem Template maskiert. Es wird durch einen einfachen Parameter in der Applikations-settings.yml aktiviert.

Output Escaping aktivieren

Output Escaping wird global für eine Applikation in der settings.yml konfiguriert. Zwei Parameter bestimmen, wie Output Escaping arbeitet: Die Strategie bestimmt, wie die Variablen der View zur Verfügung gestellt werden, und die Methode ist die Standard-Maskierfunktion, die auf die Daten angewendet wird.

Die nächsten Abschnitte beschreiben diese Einstellungen detailliert, aber grundsätzlich ist das einzige, was Sie tun müssen, um Output Escaping zu aktivieren, den escaping_strategy-Parameter von seinem Standardwert bc auf both zu ändern wie in Codeabschnitt 7-42.

Codeabschnitt 7-42 - Output Escaping aktivieren in frontend/config/settings.yml

all:
  .settings:
    escaping_strategy: both
    escaping_method:   ESC_ENTITIES

Jetzt wird htmlentities() standardmäßig auf alle Ausgabevariablen angewandt. Wenn Sie in einer Aktion eine test-Variable wie folgt definieren:

[php]
$this->test = '<script>alert(document.cookie)</script>';

Dann wird diese Variable im Template wie folgt ausgegeben:

[php]
echo $test;
 => &gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;

Wenn Sie Output Escaping aktiviert haben, haben Sie in jedem Template Zugriff auf die Variable $sf_data. Es handelt sich dabei um ein Containerobjekt, das all die maskierten Variablen enthält. Sie können die Testvariable also auch so ausgeben:

[php]
echo $sf_data->get('test');
=> &gt;&lt;script&gt;alert(document.cookie)&lt;/script&gt;

TIPP Das $sf_data-Objekt verwendet Array-Syntax, Sie können also anstatt $sf_data->get('meinevariable') die maskierten Parameter über $sf_data['meinevariable'] erreichen. Es handelt sich aber eigentlich nicht um ein Array, weshalb Funktionen wie print_r() nicht funktionieren werden.

Dieses Objekt bietet Ihnen auch Zugriff zu den unmaskierten Rohdaten. Das ist hilfreich, wenn eine Variable HTML-Code enthält, der vom Browser interpretiert werden sollen, vorausgesetzt, Sie vertrauen dieser Variablen. Rufen Sie die getRaw()-Methode auf, wenn Sie die Rohdaten ausgeben möchten.

[php]
echo $sf_data->getRaw('test');
 => <script>alert(document.cookie)</script>

Jedesmal, wenn Sie möchten, dass HTML-Variablen vom Browser interpretiert werden sollen, benötigen Sie die Rohdaten. Das ist auch der Grund, weshalb das Standardlayout $sf_data->getRaw('sf_content') verwendet, um ein Template einzubinden. Nur $sf_content funktioniert nämlich nicht, wenn Output Escaping aktiviert ist..

Escaping-Strategie

Die escaping_strategy-Einstellung bestimmt, wie Variablen standardmäßig ausgegeben werden. Die folgenden Werte sind möglich:

  • bc (backward compatible, rückwärts-kompatibel): Variablen werden nicht maskiert, aber eine maskierte Version jeder Variablen ist im $sf_data-Container verfügbar. Die Daten sind also standardmäßig im Rohformat, außer wenn Sie über das $sf_data-Objekt auf die maskierten Daten zugreifen. bc ist die Standardeinstellung und Sie sollten sich bewusst sein, dass mit dieser Strategie Ihre Seite anfällig für XSS-Attacken ist.
  • both: Alle Variablen werden standardmäßig maskiert. Die Werte sind auch im $sf_data-Container verfügbar. Das ist die empfohlene Strategie, da Sie nur ein Risiko eingehen, wenn Sie absichtlich die Rohdaten ausgeben. Manchmal werden Sie unmaskierte Daten verwenden müssen – beispielsweise, wenn eine Variable HTML-Code enthält, der gerendert werden soll. Passen Sie also auf: Wenn Sie während der Entwicklung einer Applikation zu dieser Strategie wechseln, funktionieren gewisse Dinge womöglich nicht mehr. Die beste Idee ist es daher, diese Einstellung gleich von Beginn an zu verwenden.
  • on: Werte sind nur im $sf_data-Container verfügbar. Das ist der sicherste und schnellste Weg, da Sie bei jedem Ausgeben einer Variablen auswählen müssen, ob Sie die maskierte Version mit get() oder die Rohdaten mit getRaw() ausgeben. Sie sind sich also jederzeit der Möglichkeit bewusst, dass die Daten korrumpiert sein könnten.
  • off: Schaltet Output Escaping vollständig ab. Der $sf_data-Container ist in den Templates nicht verfügbar. Sie können diese Strategie anstelle von bc verwenden, um Ihre Applikation zu beschleunigen, wenn Sie sich sicher sind, dass Sie niemals maskierte Daten benötigen.

Escaping-Helfer

Escaping-Helfer sind Funktionen, die eine maskierte Version ihrer Eingabe zurückgeben. Sie können als Standard-escaping_method in der setting.yml verwendet werden oder für einen bestimmten Wert in der View. Die folgenden Escaping-Helfer sind verfügbar:

  • ESC_RAW: Maskiert den Wert nicht.
  • ESC_ENTITIES: Wendet die PHP-Funktion htmlentities() mit ENT_QUOTES als zweitem Parameter auf die Eingabe an.
  • ESC_JS: Maskiert einen Wert, der in einem JavaScript-String als HTML verwendet wird. Hilfreich, wenn HTML dynamisch mit JavaScript verändert wird.
  • ESC_JS_NO_ENTITIES: Maskiert einen Wert, der in einen JavaScript-String gehört, aber fügt keine Entities hinzu. Das ist hilfreich, wenn der Wert z.B. in einer Dialogbox angezeigt wird. (Eine meinString-Variable wird angezeigt mit javascript:alert(meinString).)

Arrays und Objekte maskieren

Output Escaping funktioniert nicht nur bei Strings, sondern auch bei Arrays und Objekten. Jeder Wert in einem Objekt oder Array wird seinen maskierten Zustand an seine Kinder weitergeben. Nehmen wir an, Ihre Strategie sei both. Dann zeigt Codeabschnitt 7-43 die Maskierungskaskade.

Codeabschnitt 7-43 - Escaping funktioniert auch bei Arrays und Objekten

[php]
// Klassendefinition
class meineKlasse
{
  public function testeSpezialZeichen($wert = '')
  {
    return '<'.$wert.'>';
  }
}

// In der Aktion
$this->test_array = array('&', '<', '>');
$this->test_array_of_arrays = array(array('&'));
$this->test_objekt = new meineKlasse();

// Im Template
<?php foreach($test_array as $wert): ?>
  <?php echo $wert ?>
<?php endforeach; ?>
 => &amp; &lt; &gt;
<?php echo $test_array_of_arrays[0][0] ?>
 => &amp;
<?php echo $test_objekt->testeSpezialZeichen('&') ?>
 => &lt;&amp;&gt;

Die Variablen im Template sind jedoch nicht von dem Typ, den Sie womöglich erwarten. Das Output-Escaping-System "dekoriert" sie und verwandelt sie in spezielle Objekte:

[php]
<?php echo get_class($test_array) ?>
 => sfOutputEscaperArrayDecorator
<?php echo get_class($test_objekt) ?>
 => sfOutputEscaperObjectDecorator

Das erklärt auch, warum einige Standard-PHP-Funktionen (wie array_shift(), print_r() usw.) bei maskierten Arrays nicht mehr funktionieren. Aber Sie können darauf immer noch mit [] zugreifen, sie mit foreach traversieren, und die Anzahl mit count() zurückgeben (wobei letzteres nur mit PHP 5.2 oder neuer funktioniert). Und in Templates sollten die Daten ohnehin schreibgeschützt sein, die meisten Zugriffe werden also auf Arten geschehen, die funktionieren.

Sie haben immer noch eine Möglichkeit, über das $sf_data-Objekt an die Rohdaten zu gelangen. Zusätzlich werden Methoden von maskierten Objekten so verändert, dass sie einen zusätzlichen Parameter akzeptieren: Eine Maskierungsmethode. Sie können also eine andere Maskierungsmethode wählen jedesmal, wenn Sie eine Variable in einem Template anzeigen, oder Sie können mit dem ESC_RAW-Helfer die Maskierung komplett abschalten. Codeabschnitt 7-44 zeigt ein Beispiel.

Codeabschnitt 7-44 - Methoden von maskierten Objekten akzeptieren einen zusätzlichen Parameter

[php]
<?php echo $test_object->testeSpezialZeichen('&') ?>
=> &lt;&amp;&gt;
// Die drei folgenden Zeilen geben den gleichen Wert zurück
<?php echo $test_object->testeSpezialZeichen('&', ESC_RAW) ?>
<?php echo $sf_data->getRaw('test_object')->testeSpezialZeichen('&') ?>
<?php echo $sf_data->get('test_object', ESC_RAW)->testeSpezialZeichen('&') ?>
 => <&>

Wenn Sie in Ihren Templates mit Objekten arbeiten, werden Sie den Trick mit dem zusätzlichen Parameter häufig verwenden, da er die schnellste Möglichkeit bietet, Rohdaten direkt aus einem Methodenaufruf zu bekommen.

ACHTUNG Die üblichen Symfony-Variablen werden ebenfalls maskiert, wenn Sie Output Escaping aktivieren. Achten Sie also darauf: $sf_user, $sf_request, $sf_param und $sf_context funktionieren immer noch, aber ihre Methoden geben maskierte Daten zurück – es sei denn, Sie geben ESC_RAW als letzten Parameter bei den Methodenaufrufen an.

TIPP Neu in Symfony 1.1: Auch wenn XSS der häufigste Exploit bei Webseiten ist, ist es bei weitem nicht der einzige. CSRF ist ebenfalls weit verbreitet, und Symfony ermöglicht es Ihnen auf einfache Art, Ihre Anwendung zu schützen. Lesen Sie die Sidebar "Die CSRF-Filter" in Kapitel 6 für weitere Informationen.

Zusammenfassung

Viele verschiedene Tools stehen Ihnen zur Verfügung, um die Präsentationsschicht zu bearbeiten. Template erstellen Sie dank der Helfer innert Sekunden. Layouts, Partials, Komponenten und Komponentenslots bringen sowohl Modularität als auch Wiederverwendbarkeit. Die Viewkonfiguration bedient sich der Geschwindigkeitvorteile von YAML, um die (meisten) Seitenheader zu verwalten. Die Konfigurationskaskade bewahrt Sie davor, jede Einstellung für jede View zu definieren. Für jede Änderung der Präsentation, die von dynamischen Daten abhängt, hat die Aktion Zugriff auf das sfResponse-Objekt. Und die Sicht ist dank dem Output-Escaping-System sicher vor XSS-Angriffen.