Development

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

You must first sign up to be able to contribute.

Version 17 (modified by Stefan.Koopmanschap, 10 years ago)
better title markup, navigation through chapters, small typo fix

Hoofdstuk 2 - Symfony's Code Ontdekken

Op het eerste gezicht is de code van een symfony-gedreven applicatie misschien intimiderend. Het bestaat uit veel directories en scripts, en de bestanden zijn een mix van PHP classes, HTML en soms zelfs een mix van die twee. Je ziet ook veel verwijzingen naar classes die je nergens in de applicatie directory kan vinden, en de directories gaan wel zes niveaus diep. Maar wanneer je de redenering achter deze in eerste instantie nogal complexe structuur begrijpt, dan voelt het in een keer zo natuurlijk aan dat je de symfony applicatie structuur niet meer wil inruilen voor enige andere structuur. Dit hoofdstuk zorgt ervoor dat het geintimideerde gevoel verdwijnd door de structuur uit te leggen.

Het MVC Patroon

Symfony is gebaseerd op het klassieke web design pattern dat bekend staat als de MVC architectuur. Dit bestaat uit drie lagen:

  • Het model representeert de informatie waarop de applicatie draait - de business logica.
  • De view verandert het model in een web pagina die geschikt is voor interactie met de gebruiker.
  • De controller reageert op gebruikersinteractie en roept de benodigde veranderingen in het model of de view aan.

Figuur 2-1 illustreert het MVC patroon.

De MVC architectuur scheidt de business logica (model) van de presentatie logica (view), wat resulteert in een hogere beheersbaarheid. Bijvoorbeeld, als je applicatie moet draaien op zowel standaard web browsers en handheld apparaten, dan heb je een nieuwe view nodig; je kan dan wel de originele controller en het originele model gebruiken. De controller helpt om het detail van het protocol van je request (HTTP, console, e-mail enz) te verbergen van het model en de view. En het model maakt de logica van de data abstract, waardoor de view onafhankelijk wordt van, bijvoorbeeld, het type database dat door de applicatie gebruikt wordt.

Figuur 2-1 - Het MVC patroon
http://www.symfony-project.com/images/book/trunk/F0201.png

MVC Legering

Om je te helpen de voordelen van MVC te begrijpen, kijken we hoe een basis PHP applicatie naar een MVC-gebaseerde applicatie kan worden omgezet. Een lijst met berichten voor een weblop applicatie is een goed voorbeeld.

Standaard programmeren

In een standaard PHP bestand kan het presenteren van een lijst met database records eruit zien als in listing 2-1.

Listing 2-1 - Een standaard script

<?php
 
// Verbinden, selectie database
$link = mysql_connect('localhost', 'gebruikersnaam', 'wachtwoord');
mysql_select_db('blog_db', $link);
 
// SQL query uitvoeren
$result = mysql_query('SELECT datum, titel FROM bericht', $link);
 
?>
 
<html>
  <head>
    <title>Lijst met berichten</title>
  </head>
  <body>
   <h1>Lijst met berichten</h1>
   <table>
     <tr><th>Datum</th><th>Titel</th></tr>
<?php
// Resultaten printen in HTML
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
  echo "\t<tr>\n";
  printf("\t\t<td> %s </td>\n", $row['datum']);
  printf("\t\t<td> %s </td>\n", $row['titel']);
  echo "\t</tr>\n";
}
?>
    </table>
  </body>
</html>
 
<?php
 
// Sluiten verbinding
mysql_close($link);
 
?>

Dat is snel geschreven, draait snel en onmogelijk te onderhouden. De volgende punten omschrijven de belangrijkste problemen met deze code:

  • Er is geen foutcontrole en -afhandeling (wat gebeurt er als de verbinding met de database mislukt?).
  • HTML en PHP code lopen door elkaar en is zelfs verweven.
  • De code draait alleen op een MuSQL database.

Het isoleren van de presentatie

De echo en printf aanroepen in Listing 2-1 maakt de code moeilijk om te lezen. Het aanpassen van de HTML code om de presentatie te verbeteren is een gedoe met de huidige syntax. Dus kan de code opgesplitst worden in twee delen. Eerst, de pure PHP code met alle business logica in een controller script, zoals aangegeven in listing 2-2.

Listing 2-2 - Het Controller gedeelte, in index.php

<?php
 
 // verbinding maken en het selecteren van de database
 $link = mysql_connect('localhost', 'gebruikersnaam', 'wachtwoord');
 mysql_select_db('blog_db', $link);
 
 // Uitvoeren van de sql query
 $result = mysql_query('SELECT date, title FROM post', $link);
 
 // Vullen van een array voor de view
 $posts = array();
 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
 {
    $posts[] = $row;
 }
 
 // Sluiten van de databaseverbinding
 mysql_close($link);
 
 // Requiren van de view
 require('view.php');
 
 ?>

De HTMl code bevat een template-achtige PHP syntax, en zit in een view script, zoals weergegeven in Listing 2-3.

Listing 2-3 - The View Part, in view.php

<html>
  <head>
    <title>List of Posts</title>
  </head>
  <body>
    <h1>List of Posts</h1>
    <table>
      <tr><th>Date</th><th>Title</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>

Een goed ezelsbruggetje om te bepalen of je view schoon genoeg is, is dat het een minimum aan PHP code moet bevatten om door een HTML designer zonder PHP kennis begrepen te worden. De meest voorkomende statements in views zijn echo, if/endif, foreach/endforeach, en dat is het eigenlijk wel. Ook dient er geen PHP code te zijn die HTML echoot.

Alle logica wordt verplaatst naar het controller script, en bevat pure PHP met geen enkele HTML. Hetzelfde controller script zou in theorie ook gebruikt moeten kunnen worden voor een compleet andere presentatie, zoals een PDF bestand of een XML structuur.

Het isoleren van de Data Manipulatie

Het grootste deel van het controller script wordt gebruikt voor data manipulatie. Maar wat als je een lijst van posts in een andere controller moet hebben, bijvoorbeeld een die een RSS feed genereert van de weblog posts? Wat als je alle database queries in een plek wil hebben, om te voorkomen dat er dubbele code ontstaat? Wat als je beslist het data model aan te passen en de post tabel te hernoemen naar weblog_post? Wat als je wil wisselen naar PostgreSQL in plaats van MySQL? Om dit allemaal mogelijk te maken moet je de data manipulatie code uit de controller verwijderen en dit in een ander script stoppen. Dit script is het model, en is weergegeven in Listing 2-4.

Listing 2-4 - Het Model gedeelte, in model.php

<?php
 
function getAllPosts()
{
  // Verbinding opzetten en database selecteren
  $link = mysql_connect('localhost', 'gebruikersnaam', 'wachtwoord');
  mysql_select_db('blog_db', $link);
 
  // SQL query uitvoeren
  $result = mysql_query('SELECT date, title FROM post', $link);
 
  // Array vullen
  $posts = array();
  while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
  {
     $posts[] = $row;
  }
 
  // verbinding sluiten
  mysql_close($link);
 
  return $posts;
}
 
?>

De aangepaste controller is te vinden in Listing 2-5.

Listing 2-5 - Het controller gedeelte, aangepast, in index.php

<?php
 
// Requiren van het model
require_once('model.php');
 
// Ophalen van een lijst van alle posts
$posts = getAllPosts();
 
// Requiren van de view
require('view.php');
 
?>

De controller wordt nu een stuk makkelijker te lezen. De enige taak hiervan is nu het ophalen van de data uit het model, en deze data door te geven aan de view. In meer complexe applicaties kan de controller zich ook bezig houden met de request, de gebruikerssessie, de authenticatie en dergelijke. het gebruik van uitgebreide namen voor de functies binnen het model maakt commentaar van de code in de controller overbodig.

Het model script is volledig gewijd aan data toegang en kan als zodanig georganiseerd worden. Alle parameters die niet afhankelijk zijn van de data laag (zoals de request parameters) moeten door de controller worden doorgegeven, en niet direct door het model worden benaderd. De model functies kunnen dan makkelijk door een andere controller worden hergebruikt.

Scheiden van Lagen Voorbij MVC

Het principe van de MVC architectuur is dus het scheiden van de code in drie lagen, afhankelijk van de natuur van de code. Data logica code in het model, presentatie code in de view en applicatie logica in de controller.

Andere design patterns maken je programmeer ervaring nog makkelijker. De model, view en controller lagen kunnen nog verder worden onderverdeeld.

Database Abstractie

De model laag kan worden opgesplitst in een data-toegangslaag en een database abstractie laag. Op die manier gebruikt de data-toegangslaag geen database-afhankelijke query statements, maar roept andere functies aan die de queries uitvoeren. Als je later van database systeem verandert, hoeft alleen de database abstractie laag te worden aangepast.

Een voorbeeld van een MySQL-specifieke data-toegangslaag kan je vinden in Listing 2-6, gevolgd door een voorbeeld database abstractie laag in Listing 2-7.

Listing 2-6 - Het Database Abstractie Deel van het Model

<?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);
}

Listing 2-7 - Het Data-toegangsdeel van het Model

<?php
function getAllPosts()
{
  // Connecting to database
  $link = open_connection('localhost', 'myuser', 'mypassword');
 
  // Performing SQL query
  $result = query_database('SELECT date, title FROM post', 'blog_db', $link);
 
  // Filling up the array
  $posts = array();
  while ($row = fetch_results($result))
  {
     $posts[] = $row;
  }
 
  // Closing connection
  close_connection($link);
 
  return $posts;
}
 
?>

Je kan zien dat er geen database-engine afhankelijke functies worden gebruikt in de data-toegangslaag, waardoor het database-onafhankelijk wordt. Daarnaast kunnen de functies die worden gemaakt in de database abstractie laag worden hergebruikt voor veel andere model functies die toegang tot de database nodig hebben.

De voorbeelden in Listing 2-6 en 2-7 zijn nog steeds niet heel erg bevredigend, en er moet nog wat werk gedaan worden op een volledige database abstractie te bewerkstelligen (bijvoorbeeld door de SQL code te laten genereren door een database-onafhankelijke query bouwer, het verplaatsen van alle functies naar een class, en dergelijke). Maar het doel van dit boek is niet om je te tonen hoe je alle code handmatig kan schrijven, en je zult in hoofdstuk 8 zien dat symfony van nature de abstractie zeer goed uitvoert.

View Elementen

De view laag kan ook voordeel halen uit scheiding van code. Een web pagina bevat vaak elementen die consistent zijn door de hele applicatie: de pagina headers, de grafische layout, de footer, en de globale navigatie. Alleen het binnenste deel van een pagina verandert. Dat is de reden dat de view wordt gescheiden in de layout en de template. De layout is normaal gesproken globaal voor de applicatie, of een groep van pagina's. De template geeft alleen vorm aan de variabelen die door de controller beschikbaar zijn gesteld. Enige logica is nodig om deze componenten samen te laten werken, en deze laag om te bekijken krijgt daarom de naam 'view'. Deze principes volgend kan de view in Listing 2-3 opgesplitst worden in drie delen, zoals getoond in Listings 2-8, 2-9 en 2-10.

Listing 2-8 - Het template deel van de view, in mijntemplate.php

<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post['date'] ?></td>
    <td><?php echo $post['title'] ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-9 - Het view logica deel van de view

<?php
 
$title = 'Lijst van Posts';
$content = include('mijntemplate.php');
 
?>

Listing 2-10 - Het layout deel van de view

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

Action en Front Controller

De controller doet niet veel in het vorige voorbeeld, maar in echte web applicaties moet de controller veel werk doen. Een belangrijk deel van dit werk wordt door alle controllers van een applicatie uitgevoerd. Deze veelvoorkomende taken zijn het afhandelen van de request, beveiliging, laden van de configuratie, en dergelijke. Dit is de reden dat een controller vaak wordt opgedeeld in een front controller, die uniek is voor de applicatie, en actions, die alleen de controller code bevatten voor een specifieke pagina.

Een van de grootste voordelen van een front controller is dat het een unieke ingang bied voor de volledige applicatie. Als je ooit besluit om de toegang tot de applicatie af te sluiten hoef je alleen het front controller script te wijzigen. In een applicatie zonder controller moet je alle individuele controllers uitzetten.

Object Orientatie

Alle eerdere voorbeelden gebruiken procedureel programmeren. De OOP mogelijkheden van moderne talen maakt het programmeren nog makkelijker, omdat objecten logica kunnen bevatten, kunnen overerven van anderen, en schone benamingsconventies kunnen bieden.

Het implementeren van een MVC architectuur in een niet object-georienteerde taal veroorzaakt namespace en code-duplicatie problemen, en de code is in het algemeen moeilijk om te lezen.

Object orientatie staat ontwikkelaars toe om dingen als het view object, het controller object en de model classes te gebruiken, en om de eerder getoonde functies in methodes te veranderen. Het is een must voor MVC architecturen.

Als je meer wil leren over design patterns voor web applicaties in een object-georienteerde context, lees dan "Patterns of Enterprise Application Architecture" van Martin Fowler (Addison-Wesley, ISBN: 0-32112-742-0). Code voorbeelden in het boek van Fowler zijn in Java en C#, maar zijn nog steeds goed leesbaar voor PHP ontwikkelaars.

Symfony's MVC Implementatie

Maar wacht even. Voor een enkele pagina met een lijst met posts in een weblog, hoeveel componenten zijn daarvoor nodig? Zoals in figuur 2-2 geillustreerd, hebben we de volgende onderdelen:

  • Model laag
    • Database abstractie
    • Data toegang
  • View laag
    • View
    • Template
    • Layout
  • Controller laag
    • Front controller
    • Action

Zeven scripts--dat zijn heel wat bestanden om te openen en aan te passen bij iedere nieuwe pagina! Symfony maakt dingen echter makkelijk. Bij het gebruik van de MVC architectuur implementeert symfony dit op zo'n manier dat applicatie ontwikkeling snel en pijnloos is.

Allereerst is de front controller en de layout globaal voor alle acties in een applicatie. Je kan meerdere controllers en layouts hebben, maar je hebt er maar 1 van elk nodig. De front controller is pure MVC logica, en je hoeft er nooit een te schrijven, want symfony genereert deze voor je.

Het andere goede nieuws is dat de classes van de model laag ook worden gegenereert, op basis van je data structuur. Dit is het werk van de Propel library, die class skeletons en code generatie bied. Als Propel foreign keys of datum velden vind, zal het speciale toegangs- en mutatiemethoden aanbieden die data manipulatie heel erg makkelijk maken. En de database abstractie is compleet onzichtbaar voor je, omdat dit wordt afgehandeld door een ander component, genaamd Creole. Dus als je besluit om je database engine aan te passen, dan hoef je geen enkele regel code te herschrijven. Je hoeft alleen maar een configuratie parameter aan te passen.

En het laatste punt is dat de view logica makkelijker vertaald kan worden als een simpel configuratiebestand, waarbij geen programmeren meer nodig is.

Figure 2-2 - Symfony workflow
http://www.symfony-project.com/images/book/1_0/F0202.png

Dat betekent dat de lijst van posts omschreven in ons voorbeeld maar drie bestanden nodig heeft om te werken in symfony, zoals Listings 2-11, 2-12 en 2-13 laten zien.

Listing 2-11 - lijst Action, in myproject/apps/myapp/modules/weblog/actions/actions.class.php

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

Listing 2-12 - lijst Template, in myproject/apps/myapp/modules/weblog/templates/listSuccess.php

<h1>List of Posts</h1>
<table>
<tr><th>Date</th><th>Title</th></tr>
<?php foreach ($posts as $post): ?>
  <tr>
    <td><?php echo $post->getDate() ?></td>
    <td><?php echo $post->getTitle() ?></td>
  </tr>
<?php endforeach; ?>
</table>

Listing 2-13 - lijst View, in myproject/apps/myapp/modules/weblog/config/view.yml

listSuccess:
  metas: { title: List of Posts }

Daarnaast zal je nog steeds een layout moeten definieren, zoals wordt aangegeven in Listing 2-14, maar deze zal veel worden hergebruikt.

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

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

En dat is eigenlijk alles wat je nodig hebt. Dit is de exacte code die nodig is om dezelfde pagina te tonen als het simpele script eerder in Listing 2-1. De rest (alle componenten laten samenwerken) wordt afgehandeld door symfony. Als je de regels telt, zal je zien dat het maken van een lijst van posts in een MVC architectuur met symfony niet meer tijd of programmeerwerk zal vergen als het schrijven van een "plat" bestand. Desalniettemin geeft het je veel grote voordelen, zoals duidelijke code organisatie, herbruikbaarheid, flexibiliteit en veel meer leuks. En als een bonus heb je XHTML ondersteuning, debug mogelijkheden, makkelijke configuratie, database abstractie, slimme URL routing, meerdere omgevingen, en veel meer ontwikkel tools.

Symfony Kern Classes

De MVC implementatie in symfony gebruikt een aantal classes die je vaak zal tegenkomen in dit boek:

  • sfController is de controller class. Deze decodeert de request en geeft het aan de action.
  • sfRequest slaat alle request elementen op (parameters, cookies, headers en dergelijke).
  • sfResponse bevat de response header en inhoud. Dit is het object dat uiteindelijk wordt omgezet naar een HTML response en wordt verstuurd aan de gebruiker.
  • De context singleton (gehaald uit sfContext::getInstance()) slaat een referentie op naar alle kern objecten en de huidige configuratie; het is overal toegankelijk.

Je zal meer leren over deze objecten in Hoofdstuk 6.

Zoals je kunt zien gebruiken alle symfony classes de sf prefix, en ook de symfony kernvariabelen in de templates. Dit zou botsingen in naamgeving binnen je eigen classes en variabelen moeten voorkomen, en maakt de kern framework classes gegroepeerd en makkelijk te herkennen.

Tussen de programmeerstandaard gebruikt in symfony, UpperCamelCase? is de standaard voor class en variabele benaming. Er bestaan twee uitzonderingen: kern symfony classes beginnen met sf, allemaal kleine letters, en variabelen gebruikt in templates gebruiken de underscore-gescheiden syntax.

Code Organisatie

Nu dat je de verschillende componenten van een symfony applicatie kent, vraag je je waarschijnlijk af hoe ze zijn georganiseerd. Symfony organiseert de code in een project structuur en plaatst de project bestanden binnen een standaard boomstructuur.

Project Structuur: Applicaties, Modules en Acties

In symfony is een project een set diensten en operaties die beschikbaar zijn onder een bepaalde domeinnaam, die allemaal hetzelfde object model delen.

Binnen een project zijn de operaties logisch gegroepeerd in applicaties. Een applicatie kan normaal gesproken onafhankelijk van andere applicaties van hetzelfde project draaien. In de meeste gevallen zal een project twee applicaties bevatten: een voor de frontend, en een voor de backend, die beiden dezelfde database delen. Maar je kan ook een project hebben die allerlei mini-sites bevat, ieder met een andere applicatie. Let wel op dat hyperlinks tussen verschillende applicaties in een absolute vorm moeten worden gemaakt.

Elke applicatie is een set van een of meer modules. Een module staat normaal gesproken gelijk aan een pagina of een groep pagina's met een soortgelijk doel. Je kan bijvoorbeel modules hebben als home, artikelen, help, winkelWagen, account, en dergelijke.

Modules bevatten acties, die de verschillende acties binnen een module representeren. Een winkelWagen module kan bijvoorbeeld een toevoeg, toon en bijwerken actie bevatten. Over het algemeen genomen kunnen acties worden omschreven met een werkwoord. Het werken met acties is bijna hetzelfde als het werken met pagina's in een klassieke web applicatie, hoewel twee acties kunnen resulteren in dezelfde pagina (bijvoorbeeld het toevoegen van commentaar bij een weblog-post zal dezelfde post weer tonen, maar dan met het nieuwe commentaar).

Als dit te veel lagen representeert voor een beginnend projoect, kan je het heel makkelijk allemaal groeperen in een enkele module, zodat de bestandsstructuur simpel blijft. Wanneer de applicatie meer complex wordt, zal het tijd zijn om de acties te reorganiseren naar aparte modules. Zoals in Hoofdstuk 1 genoemd, herschrijven van code om de structuur of leesbaarheid te verbeteren (zonder het gedrag te veranderen) wordt refactoring genoemd, en dit zal je vaak doen als je ontwikkelt volgens de RAD principes.

Figuur 2-3 toont een voorbeeld code organisatie voor een weblog project, in een project/applicatie/module/actie structuur. Maar wees ervan bewust dat de daadwerkelijke bestandsboomstructuur van het project anders zal zijn dan de opzet in dit figuur.

Figuur 2-3 - Voorbeeld van code organisatie

http://www.symfony-project.com/images/book/1_0/F0203.png

Bestands Boomstructuur

Alle web projecten delen over het algemeen dezelfde types inhoud, zoals de volgende:

  • Een database, zoals MySQL of PostgreSQL
  • Statische bestanden (HTML, platjes, JavaScript bestanden, style sheets, enzovoorts)
  • Bestanden geupload door gebruikers of beheerders van de site
  • PHP classes en libraries
  • Externe libraries (door derden)
  • Batch bestanden (scripts die via de commandline of een cron tabel worden uitgevoerd)
  • Log bestanden (die worden geschreven door de applicatie of de server)
  • Configuratie bestanden

Symfony bied een standaard bestandsboomstructuur om alle deze inhoud op een logische manier te organiseren, consistent met de keuzes op het gebied van architectuur (MVC patroon en project/applicatie/module groepering). Dit is de boomstructuur die automatisch wordt aangemaakt wanneer een project, applicatie of module wordt geinitialiseerd. Natuurlijk kan je het volledig aanpassen aan je wensen, om bestanden naar je eigen voorkeur te plaatsen of om aan de eisen van je klant te voldoen.

Root Boomstructuur

Dit zijn de directories die je vind in de root van een symfony project:

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

Tabel 2-1 omschrijft de inhoud van deze directories.

Tabel 2-1 - Root Directories

Directory Omschrijving
apps/ Bevat een directory per applicatie in het project (normaal gesproken, frontend en backend)
batch/ Bevat PHP scripts die via de commandline of een cron table gedraaid worden, om batch processen uit te voeren
cache/ Bevat de gecachete versie van de configuratie en (als je het geactiveerd hebt) een gecachete versie van de actions en templates van het project. Het cache mechanisme (omschreven in Hoofdstuk 12) gebruikt deze bestanden om de response voor web aanvragen te versnellen. Elke applicatie zal hier een subdirectory hebben, dat voorbereidde PHP en HTML bestanden bevat.
config/ Bevat de algemene configuratie van het project
data/ Hier kan je alle data bestanden van je project plaatsen, zoals database schemas, SQL bestanden die tabellen genereert, of zelfs een SQLite database bestand
doc/ Bevat de project documentatie, inclusief je eigen documenten en de documentatie gegenereerd door PHPdoc.
lib/ Toegewezen voor externe classes of libraries. Hier kan je code toevoegen die gedeeld moet worden door al je applicaties. De model/ subdirectory bevat het object model van het project (zoals beschreven in Hoofdstuk 8)
log/ Bevat de van toepassing zijnde log bestanden die door symfony worden aangemaakt. Indien gewenst kan deze directory ook de web server log bestanden, database log bestanden of log files van enig ander onderdeel van het project bevatten. Symfony maakt een log bestand per applicatie en omgeving aan (log files worden besproken in Hoofdstuk 16)
plugins/ Bevat de plugins die zijn geinstalleerd in de applicatie (plug-ins worden besproken in Hoofdstuk 17)
test/ bevat unit en functionele tests geschreven in PHP en compatible met het symfony test framework (besproken in Hoofdstuk 15). Tijdens de opzet van een project voegt symfony automatisch wat korte voorbeelden voor voor een paar basis testes.
web/ De root voor de web server. Dit zijn de enige bestanden die via het Internet beschikbaar zijn.

Applicatie Boomstructuur

De boomstructuur van alle applicatie directories is hetzelfde:

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

Tabel 2-2 omschrijft de applicatie subdirectories.

Table 2-2 - Applicatie Subdirectories

Directory Omschrijving
config/ Bevat een aardige set met YAML configuratiebestanden. Dit is waar het grootste deel van de applicatie configuratie is, los van de standaard parameters die in het framework zelf gevonden kunnen worden. Let wel op dat ook de standaard waardes kunnen worden overschreven indien nodig. Je zal meer leren over applicatie configuratie in Hoofdstuk 5
i18n/ Bevat bestanden gebruikt voor de internationalisatie van de applicatie -- hoofdzakelijk interface vertalingsbestanden (Hoofdstuk 13 omschrijft internationalisatie). Je kan langs deze directory werken indien je ervoor kiest een database te gebruiken voor internationalisatie.
lib/ Bevat classes en libraries die specifiek voor deze applicatie nodig zijn
modules/ Bevat alle modules die de functionaliteiten van deze applicatie bevatten.
templates/ Bevat de globale templates voor de applicatie -- diegenen die gedeeld worden door alle modules. Standaard bevat het een layout.php bestand, dat de globale opmaak bevat waarin de module templates worden geplaatst.

De i18n/, lib/ en modules/ directories zijn leeg voor een nieuwe applicatie.

De classes van een applicatie hebben geen toegang tot methodes of attributen in andere applicaties van hetzelfde project. Hou er ook rekening mee dat hyperlinks tussen twee applicaties van hetzelfde project een absolute form moeten bevatten. Deze laatste limitatie moet je in gedachte houden tijdens de initialisatie, wanneer je kiest hoe je je project in applicaties wil verdelen.

Module Boomstructuur

Elke applicatie bevat een of meerdere modules. Elke module heeft een eigen subdirectory binnen de modules directory, en de naam van de module wordt gekozen tijdens het opzetten hiervan.

Dit is een typische boomstructuur van een module:

apps/
  [application name]/
    modules/
      [module name]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
          validate/

Tabel 2-3 omschrijft de module subdirectories.

Tabel 2-3 - Module Subdirectories

Directory Omschrijving
actions/ Bevat over het algemeen een enkel class bestand genaamd actions.class.php, waarin je alle actions van een module kunt toevoegen. Je kan ook verschillende actions in aparte bestanden opslaan
config/ Kan eigengemaakte configuratie bestanden bevatten met lokale parameters voor de module.
lib/ Bevat classes en libraries specifiek voor deze module.
templates/ Bevat de templates die overeenkomen met de actions van de module. Een standaard templates, genaamd indexSuccess.php, wordt aangemaakt tijdens het opzetten van de module.
validate/ Toegewezen aan de configuratie bestanden die gebruikt worden voor formulier validatie (besproken in Hoofdstuk 10).

De config/, lib/ en validate/ directories zijn leeg voor een nieuwe module.

Web Boomstructuur

Er zijn maar heel weinig limitaties voor de web directory. In de web directory bevinden zich de publiek beschikbare bestanden. Het volgen van een aantal simpele benamingsconventies bied een standaard en een aantal handige shortcuts binnen de templates. Hier is een voorbeeld voor de structuur van een web directory:

web/
  css/
  images/
  js/
  uploads/

Standaard worden de statische bestanden verdeelt over de directories zoals besproken in Tabel 2-4.

Tabel 2-4 - Meest voorkomende Web Subdirectories

Directory Omschrijving
css/ Bevat style sheets met een .css extensie.
images/ Bevat plaatjes met een .jpg, .png of .gif formaat.
js/ Bevat JavaScript bestanden met een .js extensie.
uploads/ Moet de bestanden bevatten die door gebruikers worden geupload. Hoewel de directory normaal gesproken plaatjes bevat, is het anders dan de images directory zodat de synchronisatie van ontwikkelomgeving en productie-omgeving niet door de geuploade bestanden wordt beinvloed.

Hoewel het van harte wordt aangeraden de standaard boomstructuur te gebruiken, is het mogelijk om deze aan te passen voor specifieke wensen, zoals het toestaan een project in een server met een andere boomstructuur te plaatsen. Kijk naar Hoofdstuk 19 voor meer informatie over het aanpassen van de bestandsboomstructuur.

Veelvoorkomende Instrumenten

Een aantal technieken worden regelmatig gebruikt in symfony, en je zal deze vaak tegenkomen in dit boek en je eigen projecten. Onder andere parameter houders, constanten, en het automatisch laden van classes.

Parameter Houders

Veel van de symfony classes bevatten een parameter houder. Dit is een makkelijke manier om de attributen te omvatten met schone getter en setter methodes. Bijvoorbeeld, de sfResponse class bevat een parameter houder die je kan aanroepen met de getParameterHolder() methode. Elke parameter houder slaat gegevens op dezelfde manier op, zoals geillustreerd in Listing 2-15.

Listing 2-15 - Gebruik van de sfResponse Parameter Houder

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

De meeste classes die een parameter houder gebruiken bevatten proxy methodes om de code voor de get/set operaties kleiner te maken. Dit is onder andere het geval voor het sfResponse object, dus je kan hetzelfde doen als Listing 2-15 met de code uit Listing 2-16.

Listing 2-16 - Gebruik van de sfResponse Parameter Houden Proxy Methodes

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

De parameter houden getter accepteert een standaardwaarde als tweede argument. Dit bied een bruikbaar terugvalmechanisme dat veel preciezer is dan mogelijk met een conditionele statement. Zie Listing 2-17 voor een voorbeeld.

Listing 2-17 - Gebruik van een attribuut houder getter's standaard waarde

<?php
// De 'foobar' parameter is niet gedefinieerd, dus geeft de getter een lege waarde terug
echo $response->getParameter('foobar');
 => null
 
// Een standaard waarde kan gebruikt worden door de getter in een conditie te plaatsen
if ($response->hasParameter('foobar'))
{
  echo $response->getParameter('foobar');
}
else
{
  echo 'standaard';
}
 => standaard
 
// Maar het is veel sneller om het tweede argument hiervoor te gebruiken
echo $response->getParameter('foobar', 'standaard');
 => standaard

De parameter houders ondersteunen zelfs namespaces. Als je een derde argument meegeeft aan de setter of de getter, dan wordt dat als een namespace gebruikt, en zal de parameter alleen beschikbaar zijn binnen die namespace. Listing 2-18 bevat een voorbeeld.

Listing 2-18 - Gebruik van de sfResponse Parameter Houden Namespace

<?php
$response->setParameter('foo', 'bar1');
$response->setParameter('foo', 'bar2', 'mijn/name/space');
echo $response->getParameter('foo');
 => 'bar1'
echo $response->getParameter('foo', null, 'mijn/name/space');
 => 'bar2'

Natuurlijk kan je ook een parameter houder aan je eigen classes toevoegen om van de voordelen van de syntax faciliteiten gebruik te maken. Listing 2-19 toont hoe je een class met een parameter houder kan definieren.

Listing 2-19 - Toevoegen van een Parameter Houder aan een Class

<?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;
  }
}

Constanten

Verrassend genoeg zal je maar weinig constanten vinden in symfony. Dit is omdat in PHP constanten een groot nadeel hebben: Je kan hun waarde niet aanpassen nadat ze gedefinieerd zijn. Daarom gebruikt symfony een eigen configuratie object, genaamd sfConfig, die deze constanten vervangt. Het bied statische methodes om van overal toegang te krijgen tot parameters. Listing 2-20 demonstreert het gebruik van de sfConfig class methodes.

Listing 2-20 - Gebruik van de sfConfig class methodes in plaats van constanten

<?php
// In plaats van PHP Constanten,
define('SF_FOO', 'bar');
echo SF_FOO;
// Gebruikt symfony het sfConfig object
sfConfig::set('sf_foo', 'bar');
echo sfConfig::get('sf_foo');

De sfConfig methodes ondersteunen standaard waardes en je kan de sfConfig::set() methode vaker aanroepen om de waarde van de parameter aan te passen. Hoofdstuk 5 gaat in meer detail in op de sfConfig methodes.

Class Autoloading

Vanouds is het altijd zo geweest dat je, wanneer je een class methode gebruikt of een object aanmaakt in PHP, je eerst de class definitie moet includen.

<?php
include 'classes/MyClass.php';
$myObject = new MyClass();

Maar bij grote projecten met veel classes en een diepe directory structuur is het bijhouden van all die class bestanden en hun paden om te includen een lastige klus. Door het bieden van een autoload() functie (of een spl_autoload_register() functie), maakt symfony de include statements overbodig, en kan je direct schrijven:

<?php
$myObject = new MyClass();

Symfony zal dan zoeken naar een MyClass? definitie in alle bestanden die eindigen op php in een van de lib/ directories van het project. Als de class definitie wordt gevonden zal het deze automatisch includen.

Dus als je alle classes in de lib/ directories opslaat, dan hoef je geen classes meer te includen. Dat is waarom een symfony project normaal gesproken geen include or require statements bevat.

Voor een betere performance scant symfony een lijst met directories (gedefinieerd in een intern configuratiebestand) tijdens de eerste request. Het registreert dan alle classes die in deze directories worden gevonden en slaat deze class/bestand lijsten op in een PHP bestand als een associatieve array. Op die manier hoeven de directory scans niet meer bij elke aanroep uitgevoerd te worden. Dit is ook waarom je de cache moet legen nadat je een class toevoegt of verplaatst binnen je project. De cache leeg je met het symfony clear-cache commando. Je zal meer leren over de cache in Hoofdstuk 12, en over de configuratie van het autoloaden in Hoofdstuk 19.

Samenvatting

Het gebruik van een MVC framework dwingt je om je code te verdelen en te organiseren volgens de conventies van het framework. Presentatiecode gaat naar de view, data manipulatie gaat naar het model, en de aanvraag manipulatie logica gaat in de controller. Het maakt het gebruik van het MVC patroon zowel behulpzaam als erg limiterend.

Symfony is een MVC framework geschreven in PHP 5. De structuur van het framework is ontworpen om het beste van het MVC patroon te krijgen, met het grootste gebruiksgemak. met dank aan de uitgebreidheid en de configurabiliteit is symfony zeer bruikbaar voor alle web applicatie projecten.

Nu dat je de onderliggende theorie achter symfony kent, ben je bijna klaar om je eerste applicatie te ontwikkelen. Maar voordat je dat doet, moet je een symfony installatie op je ontwikkelomgeving werkend krijgen.

< Hoofdstuk 1 - Introductie Hoofdstuk 3 - Symfony Draaien >