WEBSITES MAKEN MET ZEND FRAMEWORK (Geschreven en gepubliceerd door Stijn Leenknegt)
VOORWOORD & INLEIDING
Websites maken met Zend Framework is een pakket geschreven door mij (stijn leenknegt) voor de mensen die websites willen maken op een hoger niveau. Zend Framework is onlangs uit beta gegaan en men kan het framework dus nu volledig gebruiken voor applicaties te maken. In het pakket gaan we een kleine portfolio schrijven mbv Zend Framework 1.0.0 (je kan ook ZF 1.0.1 gebruiken). Om deze lessen te begrijpen moet je kennis hebben van enkele zaken. Eerst en vooral moet je het MVC model begrijpen (aangezien ZF gebruik maakt van het MVC model). Daarnaast moet je over een goede kennis Object Oriented Programming beschikken omdat de php code 90% OOP is. Er is niet echt kennis van Zend Framework nodig maar het is mooi meegenomen. http://www.mindios.com/portfolio/index.php is het werkgebied van dit pakket en zal veranderen van een error naar een "mooie" portfolio. Vragen of opmerkingen ->
[email protected] Veel leesplezier Stijn Leenknegt
DE FUNDERINGEN MAKEN
1. Webserver klaarmaken We hebben een webserver nodig met volgende configuratie: ● ● ●
●
Apache (versie speelt geen rol) PHP 5.1.4 of later MySQL (of een andere database programma, hier staat MySQL omdat in dit pakket MySQL gebruikt wordt). Zend Framework 1.0.0 of later
In de public_html, www of httpdocs map van Apache maken we een map met de naam portfolio. In deze map maken we opnieuw een map met de naam library. Hierin komen alle third party codes. In de map van Zend Framework staat een map Zend. Kopieer deze naar de map library van de map portfolio. Ziezo, nu staat alles klaar om te gebruiken. Volgend punt is de website structuur maken.
2. Website structuur Bij dit punt zal je denken: wat een overdreven structuur. Wel beste lezer, dit is nu MVC op zijn best! Hieronder vind je de map structuur die wij gaan gebruiken. De mappen die vet gekleurd zijn, zijn de mappen die wel al in punt 2 gemaakt hebben en die moet je dus niet meer maken. ./portfolio ./application ./controllers ./models ./views ./helpers ./filters ./scripts ./library ./Zend ./public ./styles ./javascripts ./media ./images ./flash In de map ./public/media/ kan je allerlei media bestanden plaatsen zoals: afbeeldingen, filmpjes, flash filmpjes, PDF documenten, ...
Als je deze structuur gemaakt hebt kunnen we nu overgaan naar het volgende punt.
3. Beveiliging van de structuur De mensen mogen de inhoud van de mappen niet kunnen bekijken, dus we moeten deze beveiligen. Hiervoor gaan we gebruik maken van .htaccess bestanden. Neem volgende code over en zet deze in een .htaccess bestand. deny from all Vervolgens zet je die .htaccess bestand in de mappen ./application en ./library. In de map ./public niet omdat we anders geen publieke data kunnen toevoegen aan onze website (
zal niet werken omdat deze geblokkeert zou worden door de .htaccess). De website maakt gebruik van nette URL's en dus zullen we gebruik maken van de Apache extensie mod_rewrite. Zet volgende code in een .htaccess bestand en zet dat .htaccess bestand in de map ./portfolio.
RewriteEngine on RewriteRule .* index.php php_flag magic_quotes_gpc off php_flag register_globals off Het volgende probleem stelt zicht opnieuw voor in de map ./public. Wanneer we terug
zullen typen zal mod_rewrite dit niet begrijpen en zeggen dat hij de pagina niet kan vinden. Dus we maken een .htaccess aan in de map ./public met volgende code: RewriteEngine off Nu is onze structuur beveiligd en kunnen we gebruik maken van nette URL's. Over naar punt 4.
4. De bootstrap De bootstrap klinkt onbekend maar met de bootstrap bedoelen we de index.php. Wanneer je naar een website gaat met de url ?pagina=contact wordt in de bootstrap (index.php) de correct php ingeladen aan de hand van $_GET['pagina']. De bootstrap (begin): - set_include_path: zoals je ziet worden er 2 mappen toegevoegd aan de include path. De include path is het path waar include/require zal zoeken wanneer hij het bestand niet vindt. We hebben dus eerst onze portfolio map toegevoegd en daarna de map ./library/. Deze laatste is belangrijk omdat we niet constant moeten ./library/ typen als we iets van het Zend Framework of andere third party code willen includen. - include_once: we includen het bestand ./library/Zend/Loader.php maar aangezien ./library/ al staat in de include path moeten we die niet meer typen en typen we dus enkel Zend/Loader.php. Loader.php zorgt ervoor dat we makkelijk componenten en/of classes van het Zend Framework kunnen laden. Maar opgepast! Je kan hier ook je eigen classes mee inladen als je dat wenst. De Loader is slechts maar een component van het framework. Zend_Loader::loadClass(): Zend Loader kan je gebruiken zonder dat je een instance moet maken.
de static function loadClass kan je dus gebruiken om classen te laden. Hierin wordt de class Zend_Controller_Front geladen (werkt als include). Je denkt nu: er staat nergens een bestand Zend_Controller_Front ergens in me map en ook nergens in me library. De loadClass functie zal alle _ (underscores) vervangen door / (schuine strepen) en er .php aan toevoegen. Dus de code zal het volgende includen: Zend/Controller/Front.php en dat bestand vind je wel terug. De Zend_Front_Controller class wordt geladen omdat we die nodig hebben zodat Zend Framework de correct controller kan laden. De front controller maakt gebruik van een router class en zal door behulp van de url de correct controller class en action laden en deze uitvoeren. We gaan verder met onze bootstrap. Het enige wat nu nog moet gebeuren is het configureren van de Zend_Front_Controller. index.php: setControllerDirectory('./application/controllers'); //run the conroller $controller->dispatch(); ?> We maken een instance van de front controller en daarna zeggen we waar de controllers van onze website staan. De dispatch functie zal de request, de URL, in stukken verdelen: ● ● ● ●
Module Controller Action Toegevoegde parameters
Aan de hand van de URL zal hij de juiste class inladen en de action uitvoeren. Wanneer de dispatcher de module, controller en action niet vind zal hij de default module, controller en/of action gebruiken. Dit is standaard index maar je kan deze wijzigen door de functies setDefaultModule, setDefaultController en setDefaultAction. Let op: hier gebruiken we geen modules dus hier moet je niet naar kijken.
DE CONTROLLERS
Deel 2 gaat verder waar deel 1 gestopt is. Het laatste wat we er gebeurt was is het configureren van de controllers en dus gaat deel 2 over Controllers. Meer info over controllers vind je in de tutorial over het MVC model. Controllers zorgen ervoor dat de input van de gebruiker (url, formulier data, ...) correct verwerkt wordt (mbv Models) en dat er een correcte output op het scherm van de gebruiker komt te staan (mbv Views). De URL's gaan er altijd als volgt uitzien: http://www.mijnsite.com / controller / action / 1. De eerste controller maken De eerste controller die we maken is de IndexController. Deze moet als eerste gemaakt worden omdat wanneer de andere controller niet gevonden kan worden deze kan worden aangeroepen. Ingewikkeld? Een voorbeeldje: Stel dat een bezoeker volgende URL intikt: http://www.mijnsite.com/hack/hack , deze controller bestaat niet en dus zal de controller automatisch de default controller tonen en dat is http://www.mijnsite.com/index/index. Hetzelfde geldt ook voor de action. Stel dat de action niet gevonden wordt maar wel de controller. Dan wordt de indexAction aangeroepen van de controller. Oké we gaan nu onze IndexController maken. We nemen een leeg PHP bestand en we nemen volgende code over: IndexController.php:
De code uitleg: – class IndexController extends Zend_Controller_Action: wat? denk je. Iedere controller is een class. Deze class krijgt dezelfde naam als de naam van je php bestand (nl. IndexController). Iedere controller class moet de class Zend_Controller_Action extenden omdat in de controller class de actions worden gemaakt (en andere controller functies). – public function indexAction(): iedere action is een public function. Deze draagt altijd de naam indexAction. Dus wanneer je naar http://www.mijnsite.com/index/bewerken gaat dan wordt de action bewerkenAction() aangeroepen in de IndexController. Note: ?> moet niet op het einde van het php bestand. Sommigen weten dat niet maar je moet enkel ?> zetten in bestanden waar naast php code ook nog andere code staat (zoals html tags). Wanneer je nu gaat naar http://www.mijnsite.com/ zal de default Controller en Action aangeroepen worden, maar je ziet opnieuw die error. Je doet niets verkeerd maar dit komt omdat we onze View gedeelte nog niet gemaakt hebben. Om dit op te lossen moeten we volgende code toevoegen aan onze configuratie van de front controller in de index.php (bootstrap bestand). $controller->setParam('noViewRenderer' , true); Hiermee zeg je aan de controller dat er geen gebruik wordt gemaakt van de View. Wanneer je refresht zie je mooi de tekst van de indexAction op je scherm. 2. De controllers en actions bedenken Het is belangrijk om te weten welke pagina's je gaat maken en welke actions. Dit is de lijst met de controllers en actions die wij nu gaan maken. ●
●
IndexController ○ indexAction (de startpagina van onze portfolio) ○ infoAction (hier komt wat informatie te staan) ○ contactAction (een contact pagina) GalleryController ○ indexAction (de startpagina van de gallery, hier wordt de volledige gallery getoont) ○ viewAction (een item in detail bekijken) ○ addAction (een item toevoegen aan de gallery) ○ editAction (een item bewerken in de gallery) ○ deleteAction (een item verwijderen van de gallery)
Dat zijn niet zoveel controllers/actions maar bij een grote website kan dit al snel uitbreiden. In puntje 4 gaan we de eerste Controller verder afwerken.
3. De IndexController afwerken Als je de IndexController.php gesloten hebt moet je die opnieuw openen. We hebben al reeds de controller class gemaakt en de eerste indexAction. Nu moet je nog infoAction en contactAction toevoegen. Je kan terug volgende code overnemen of zelf eens proberen en daarna vergelijken. IndexController.php:
4. De GalleryController Nu mag je het zelf eens proberen. Volgende puntjes om je wat te helpen: ● ● ●
Plaats het php bestand in de map ./application/controllers/ De controller class extends de Zend_Controller_Action class. Voor de actions zie punt 3 van deze tutorial.
En nu is het aan jou. Wil je weten of je code correct is? Klik dan op de volgende link, http://www.plaatscode.be/6186/. Als alles goed is zal http://www.mijnsite.com/gallery/view/ een mooie tekst op het scherm toveren.
DE VIEWS
We hebben nu onze controllers al maar al wat we krijgen is een simpele tekst. In deel 3 gaan we een layout toevoegen en leer je de View van het MVC model wat beter kennen. Kennis van template parsers is goed meegenomen. Dankzij de gratis templates van http://www.devarea.nl moeten we niet te ver gaan zoeken achter een goed layout. Je kan hem downloaden door op de volgende link te klikken. Je vind er 4 bestanden (header.phtml, footer.phtml, menu.phtml en layout.css). De linken zijn al gelegd in het menu (zie deel 2: controllers). De extensie wordt in punt 2 uitgelegd. http://www.mindios.com/portfolio/public/template.zip Over naar punt 1, hier gaan we de templates bouwen en juist plaatsen. 1. Templates bouwen en organiseren Laat me eerst beginnen met de extensie. Het view gedeelte van Zend framework gebruikt de extensie phtml om aan te tonen dat het gaat om een template. phtml is een samenvoegsel van php en html. Dit gaat later héél duidelijk worden waarom. Het eerste wat er gebeurt is een nieuwe map maken. Iedere controller krijgt zijn eigen map waarin de templates komen voor de verschillende acties. Als je naar de map ./application/views/ gaat zie je 3 mappen staan (zie deel 1). Open de map scripts. Hierin maak je een nieuwe map. Deze geef je de naam index. Dat is de naam van de controller (let op de schrijfwijze: kleine letters). Je maakt ook een map aan voor de GalleryController. We hebben nu 2 mappen in de map scripts (index en gallery). Je hebt de 4 bestanden gedownload en uitgepakt ergens op je computer (of server). Volgende lijst bevat het bestand en de locatie waar dat bestand moet staan: ● ● ● ●
layout.css: /public/styles/ header.phtml: /application/views/scripts/ footer.pthml: /application/views/scripts/ menu.phtml: /application/views/scripts/
We gaan nu onze eerste pagina maken. De eerste pagina is de indexAction van IndexController. Dus we gaan onze code opslaan in de map ./application/views/scripts/index/ en geven de template de naam index.phtml (de naam van de action, kleine letters!). index.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
render('footer.phtml'); ?> Nu snap je waarom de extensie .phtml is. code uitleg: – $this->render(...): de bestanden die we zonet in de /scripts/ map geplaatst hebben worden in de template toegevoegd en weergeven op het scherm. De functie render zorgt ervoor dat we daadwerkelijk iets op het scherm te zien zullen krijgen. – echo $this->titel: net zoals andere template parsers kan je variablen plaatsen in je templates. In templatepower is dit zo: {titel} en in zend framework zijn dit php variablen. $this is een instantie van de View class in zend framework. Je gaat in het volgende punt hier meer van verstaan. In de templates kan je dus dynamische content plaatsen. Je kan gebruik maken van alle code syntacs. Je kan while lussen maken, voorwaarden maken met if/else, ... (een template parser in zijn eenvoud maar o zo goed). Onze template is gebouwd maar wat nu? In punt 2gaan we de templates leven inblazen. 2. De controllers laten de views leven We hebben nog maar één template voor de indexAction in de IndexController. We gaan de IndexController.php openen. We voegen volgende functie toe aan onze IndexController class. IndexController.php: public function init() { $this->initView(); } De init functie wordt altijd aangeroepen alvoors de action functie wordt aangeroepen.Hierin kunnen we objecten maken of initialiseren. We initialiseren nu het View gedeelte van Zend framework. Je kan het ook moeilijker maken en via Zend_Loader::loadClass('Zend_View'); de class inladen en daarna een instantie maken in iedere actie. Jij verkiest, maar deze manier is toch handiger en korter. In de bestanden header, footer en menu.phtml staan ook template variablen. Wanneer je de templates opent vind je volgende variablen terug: ● ● ●
paginaTitel stylesPath url
Omdat we die maar één keer willen schrijven, schrijven we ze direct in de init functie. Je init functie zal er nu zo uitzien (uitleg staat eronder):
IndexController.php: public function init() { $this->initView(); //variablen declaren in header,menu en footer $this->view->url = $this->_request->getBaseUrl(); $this->view->stylesPath = $this->view->url . 'public/styles/'; } code uitleg: In de templates staat er echo $this>url. Nu zei ik al dat $this een instantie is van de view component. Wanneer we de view initialiseren in de controller dan is $this>view ook een instantie van de view component. Dit betekend dat ze beide naar dezelfde class verwijzen. Dus wanneer je een variable $this>mijnVariable; zet in een template dan moet je in je controller $this>view>mijnVariable gebruiken om inhoud te geven aan die variable of om de variable inhoud te veranderen. Note: de template variable paginaTitel is niet toegevoegd in de init functie omdat deze verschilt per action (zie volgend punt). 2.1. De indexAction() Nu gaan we de andere variablen declareren en de template op het scherm “toveren”. In de indexAction() staat nu een echo. Deze mag je weg doen en volgende code plakken/typen in de plaats. IndexController.php: public function indexAction() { $this->view->paginaTitel = 'Startpagina'; $this->view->titel = 'Welkom op mijn portfolio :]'; $this->render(); } code uitleg: – De variablen declareren is niets nieuws meer voor jou. – $this>render(): je moet hier niet index.phtml of iets anders in plaatsen. De view component zal automatisch de index.phtml vinden in de ./application/views/scripts/index/ map. Hiermee wordt de complete layout getoont op het scherm. Wanneer je nu naar je website gaat, zie je dat de layout mooi op je scherm staat. Zijn er foutmeldingen of wordt de layout niet mooi weergegeven? Kijk of je template variable url correct ingevuld is (de / niet vergeten op het einde). Staan je templates correct?
Werkt het nog steeds niet? Zet dan de noViewRenderer parameter op false in de bootstrap. 3. Helpers In de views map staan er ook nog andere twee mappen: filters en helpers. De map filters blijft voor mij nog een groot vraagteken omdat hier niets over geschreven wordt in het framework manual. Er is wel een Filter component en misschien dat dit een locatie is waar je eigen gemaakte filters in kan opslaan (bij mij werkte dit niet echt goed). Helpers daarentegen maken het ontwikkelen echt een plezier. Weg met al dat HTML getyp, leve de helpers. Helpers helpen je templates sneller schrijven. Een voorbeeld: render('header.phtml'); ?> render('menu.phtml'); ?>
escape( $this->titel ); ?>
Welkom op mijn portfolio.
render('footer.phtml'); ?> Zoals je ziet wordt de template variable titel geescaped, dus “ wordt \”. De functie escape is een Helper. Hieronder staat een lijst met alle beschikbare Helpers en de uitleg: ●
● ●
● ● ● ● ● ● ●
● ● ●
declareVars(): hiermee kan je template variablen declareren zonder dat ze al een inhoud hebben. formButton($name, $value, $attribs): maak een formulier knop. $attribs is een array. formCheckbox($name, $value, $attribs, $options): maak een checkboxelement . $options is een array waarin de eerste value een vinkje (waarde: 1) krijgt. formFile($name, $value, $attribs): maak een file element. formHidden($name, $value, $attribs): maak een hidden element. formLabel($name, $value, $attribs): maak een label element. formPassword($name, $value, $attribs): maak een password element. formRadio($name, $value, $attribs, $options): maak een radio element. formReset($name, $value, $attribs): maak een reset knop. formSelect($name, $value, $attribs, $options): maak een select element. $options zijn de options. formSubmit($name, $value, $attribs): maak een submit knop. formText($name, $value, $attribs): maak een text element. formTextarea($name, $value, $attribs): maak een textarea element.
● ●
url($urlOptions, $name, $reset): maakt een link. $urlOptions zijn de link attributen. htmlList($items, $ordered, $attribs): maakt een lijst, als $ordered true is dan wordt er een OL gemaakt anders een UL. $items is een array en kan geneste items bevatten.
3.1. Schrijf je eigen Helper Je kan naast de standaard Helpers ook je eigen Helpers schrijven. Bijvoorbeeld een alert Helper of een colorText Helper. Als voorbeeld gaan we eens een colorText Helper schrijven. Hiermee kan je een kleur geven aan de tekst. Neem een nieuwe php bestand en neem volgende code over: ColorText.php: ' . $tekst . ''; } } code uitleg: – Je maakt een class aan die begint met Zend_View_Helper_ daarna de naam van je Helper (let op de hoofdletters!), zo krijgen we Zend_View_Helper_TextColor. – De class heeft minstens één functie en die heeft de naam van je Helper (let op de hoofdletters!), zo krijg je dus public function textColor. Het aantal parameters kan je zelf kiezen. – Er wordt nooit echo of print geplaatst enkel een return. Je slaat deze op als TextColor.php (let op de hoofdletters!) in de map helper. Deze Helper gebruik je nu als volgt: render('header.phtml'); ?> render('menu.phtml'); ?>
textColor( $this->titel , '#FF0000' ); ? >
Welkom op mijn portfolio.
render('footer.phtml'); ?> Zend Framework zal alle Helpers in de map Helpers automatisch laden, dus hier hoef je niet extra's te schrijven in de controllers of dergelijke. En zo kan je nu verder gaan uitbreiden en meer Helpers schrijven. 4. De andere templates bouwen We gaan nu andere templates bouwen. Volgende templates moeten nog gemaakt worden: ●
●
index ○ info.phtml ○ contact.phtml gallery ○ index.phtml ○ view.phtml ○ add.phtml ○ edit.phtml ○ delete.phtml
Vergeet geen map gallery te maken in de map ./application/views/scripts/ omdat we dit een andere controller is!!! info.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
titel; ?>
Hier vind je wat meer informatie over mijzelf en over mijn werkwijze.
render('footer.phtml'); ?>
contact.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
render('footer.phtml'); ?>
We passen de actions opnieuw aan en je IndexController zou er nu zo moeten uitzien:
initView(); //variablen declaren in header,menu en footer $this->view->url = $this->_request->getBaseUrl(); $this->view->stylesPath = $this->view->url . 'public/styles/'; } public function indexAction() {
$this->view->paginaTitel = 'Startpagina'; $this->view->titel = 'Welkom op mijn portfolio :]'; $this->render(); } public function infoAction() { $this->view->paginaTitel = 'Informatie'; $this->view->titel = 'Informatie pagina :]'; $this->render(); } public function contactAction() { $this->view->paginaTitel = 'Contact'; $this->view->titel = 'Neem contact met ons op :]'; $this->render(); } } De gallery views en controller staan op de volgende links (beetje te veel om te posten): ● ● ● ● ● ●
index.phtml: http://www.plaatscode.be/6213/ view.phtml: http://www.plaatscode.be/6214/ add.phtml: http://www.plaatscode.be/6236/ edit.phtml: http://www.plaatscode.be/6216/ delete.phtml: http://www.plaatscode.be/6217/ GalleryController.php: http://www.plaatscode.be/6218/
DE MODELS
In de gallery kunnen we projecten toevoegen, bewerken en verwijderen. We hebben al de formulieren gemaakt (zie deel 3). We moeten nu nog de data hebben. Deze data moet ergens opgeslagen worden. Dit kan via een file of een database gebeuren. Omdat het model (de M van MVC) meer database gericht is gaan we ook via deze manier gebruik maken. In deze vierde tutorial gaan we onze tabellen opzetten, templates wat aanpassen en de model schrijven. Note: kennis van PDO is niet vereist, je moet gewoon weten dat Zend Db de PDO technologie gebruikt. Note 2: als je server geen PDO ondersteund zal je helaas een andere opslag type moeten zoeken zoals XML of andere file type. Klaar? Here we go!
1. Tabellen maken MySQL is het meest gebruikte databasemodel in de php wereld en dus klinkt het niet abnormaal dat we hier voor mysql kiezen als databasemodel. De tabellen maken is simpel. Neem volgende query over en voer deze uit in je phpmyadmin. CREATE TABLE `portfolio_projects` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `titel` VARCHAR( 255 ) NOT NULL , `omschrijving` TEXT NOT NULL , `status` VARBINARY( 50 ) NOT NULL ) ;
2. Verbinding maken met de database We gaan verbinding maken met de database in onze bootstrap (index.php). Open deze en voeg volgende code toe onder lijn 11 (Zend_Loader::loadClass....): index.php: Zend_Loader::loadClass('Zend_Db'); Zend_Loader::loadClass('Zend_Db_Table'); //configure the database $options = array( 'host' => 'localhost' , 'username' => 'root' , 'password' => '********' , 'dbname' => 'database' );
$db = Zend_Db::factory( 'PDO_MYSQL' , $options ); Zend_Db_Table::setDefaultAdapter( $db ); code uitleg: – Eerst laden we de twee classes die we gebruiken. Zend_Db_Table moeten we niet gebruiken maar gebruiken we toch om models te schrijven en omdat je veel sneller queries kan schrijven. Als je enkel Zend_Db gebruikt moet je dan het PDO model gebruiken. Zend_Db_Table gebruikt het PDO model ook maar indirect. Je zal wel zien hoe en waarom. – $options: om wat overzicht te houden zettten we alle login gegevens in een array. De keys van de array moeten host, username, password en dbname zijn (anders krijg je error). – $db = Zend_Db...: met Zend_Db maken we verbinding met onze database. Zend_Db_Table kan dit niet omdat Zend_Db de hoofdclass is en die alle queries direct met de database uitvoert. Zend_Db_Table gebruikt Zend_Db in zijn functies. We hebben dus 2 parameters. De eerste is het type van database. We gebruiken mysql dus typen we PDO_MYSQL. Als je Sqlite zou gebruiken type je PDO_SQLITE. De tweede parameter is de login gegevens die in de array $options staan. – Zend_Db_Table...: zoals gezegt gebruikt Zend_Db_Table de Zend_Db class in zijn functies om queries uit te voeren. Als we geen adapter zetten kan Zend_Db_Table geen queries uitvoeren. Je index.php zou er nu ongeveer zo moeten uitzien: http://www.plaatscode.be/6226/
3. Models schrijven en resultaten ophalen Een model schrijven is het simpelste wat je maar kan doen. Voor iedere tabel moet je een model schrijven. Wij hebben één tabel (portfolio_projects) en dus hebben we ook één model voor onze site. Stel dat je 10 tabellen hebt dan zal je 10 modellen moeten schrijven. Neem volgende code over in een nieuwe php bestand: projects.php:
code uitleg: – extends Zend_Db_Table: iedere model maakt gebruik van de class Zend_Db_Table zodat we de query functies kunnen gebruiken en onze data kunnen ophalen/bewerken/verwijderen. – protected $_name...: deze variable moet je in iedere model defineren zodat Zend_Db_Table weet over welke tabel het hier gaat. 3.1. Gegevens ophalen (meerdere rijen en één rij) Onze model is geschreven en nu gaan we beginnen met de functies van Zend_Db_Table te gebruiken om gegevens op te halen. Open de GalleryController.php want net zoals in de View wordt de communicatie tussen de user (input) en de database (model) in de controllers geschreven. Aangezien iedere actie in de class GalleryController dezelfde tabel gebruiken moeten we de include van ons model maar op één plaats schrijven, nl onze init() functie. Voeg volgende code toe aan de init functie en plaats die onder de initView(); GalleryController.php: //load the model include './application/models/projects.php'; In de indexAction() worden alle projects opgehaalt en weergegeven. Zend_Db_Table heeft hier een functie voor genaamd fetchAll(). Onze indexAction() zal er zo uit gaan zien: GalleryController.php: public function indexAction() { $projects = new Projects(); //get the results and show them on the screen $this->view->projects = $projects->fetchAll(); $this->view->paginaTitel = 'Gallery'; $this->view->titel = 'Overzicht van alle projecten :]'; $this->render(); } code uitleg: We maken dus een instance van ons model. De functie fetchAll(); hebben we niet gemaakt in onze model maar deze staat al geschreven in de class Zend_Db_Table (als je verder kijkt zoals ik deed dan weet je dat het Zend_Db_Table_Abstract is want Zend_Db_Table is maar een extend ervan). We moeten de resultaten op ons scherm hebben en dus gaan we de resultaten ( fetchAll() returnt een array) in onze index template moeten schrijven. Open dus index.phtml van de map ./views/gallery/ en neem volgende code over (alleen het toegevoegde deel moet je overnemen, ¾ van de code staat al in
de index.phtml). index.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
render('footer.phtml'); ?> code uitleg: Deze manier van foreach schrijven is wat onbekend maar deze bestaat in php. Zo is het overzichtelijker om php in html te schrijven. Zoals al gezegt bevat $this>projects de array met resultaten. $row is niet zomaar een waarde van die array maar is een instantie van een object, namelijk een instantie van Zend_Db_Table_Rowset. Rowset wordt ingeschakeld als het gaat om meerdere rijen (dit kan ook 1 zijn maar het kunnen er altijd meer zijn). Je kan nu de waarden weergeven op het scherm door $row>{kolom} te echoën in je template. We hebben nu alle projecten op ons scherm staan met een mooi linkje errond. Nu gaan we in plaats van meerdere rijen, één rij ophalen. Namelijk de rij van het geselecteerde project. Volgende code voegen we toe aan onze viewAction(). GalleryController.php: public function viewAction() { $projects = new Projects(); //get id and fetch the record of the database $id = (int) $this->_request->getParam('id'); $row = $projects->fetchRow('id = ' . $id); $this->view->paginaTitel = $row->titel; $this->view->id = $row->id; $this->view->titel = $row->titel; $this->view->omschrijving = $row->omschrijving; $this->view->status = $row->status; $this->render();
} code uitleg: – $id = (int)...: in onze template index.phtml hebben we /id/{record id} toegevoegd aan onze link. Dit is nu een parameter. Je haalt deze parameter op met $this>_request>getParam(...) en tussen haakjes schrijf je de naam van de parameter, hier is dat dus id. – fetchRow(): we moeten maar één rij ophalen en dus gebruiken we de functie fetchRow() maar nu moeten we een WHERE opdracht meegeven. Normaal is dit WHERE id = $id in een gewone query en dus vullen we tussen haakjes id = $id in. fetchRow() is ook weer een functie van de Zend_Db_Table class. Ook hier moet het resultaat op het scherm komen. Dus open view.phtml en neem volgende code over: view.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
titel . '(' . $this->status . ')'; ?>
omschrijving; ?>
render('footer.phtml'); ?> Deze vereist geen verdere uitleg. Je zou deze code al zelf kunnen geschreven hebben en zekers al moeten begrijpen. We kunnen nu al onze database uitlezen. Nu nog updaten, inserten en deleten.
4. Gegevens toevoegen We hebben al een formulier gemaakt (zie deel 3) om projecten toe te voegen. We gaan nu de code schrijven om het formulier te verwerken en om de data in de database te stoppen. De action addAction() krijgt nu volgende code. GalleryController.php: public function addAction() { if( $this->_request->isPost() ) { //call model $projects = new Projects(); $data = array( 'titel' => $this->_request->getPost('titel') , 'omschrijving' => $this->_request>getPost('omschrijving') , 'status' => $this->_request->getPost('status') ); //data invoegen en de persoon redirecten $projects->insert( $data ); $this->_redirect( './gallery/' ); } else { $this->view->paginaTitel = 'Project toevoegen'; $this->view->titel = 'Voeg een project toe :]'; } $this->render(); } code uitleg: – $this>_request>isPost(): hiermee controleer je of het formulier verstuurd is of niet. Deze functie returnt een boolean met true als het formuulier gepost is. – $data = ...: we maken nu een array met de data. De keys van de array zijn de kolomnamen van de mysql tabel en de values zijn de gegevens die ingevuld zijn in het formulier. – $this>_request>getPost(...): met deze functie kan je de waarde ophalen van een formulier element. Hetgeen tussen de haakjes is de name van het formulier element. – $projects>insert( $data ): met de insert functie kan je dus records toevoegen aan je tabel. $data is de array die je zonet hebt gemaakt. Meer is er niet aan. Zie je wel dat Zend_Db_Table het scripten makkelijk/sneller maakt. – $this>_redirect(...): je kan een tekst op het scherm zetten maar je kan ook de gebruiker direct zijn resultaat laten zien door hem terug te sturen naar alle projecten.
5. Gegevens bewerken Gegevens bewerken is bijna hetzelfde als gegevens toevoegen. Het enige verschil is dat je data uit de tabel moet halen en het verwerken van het formulier is net hetzelfde als gegevens toevoegen. Het verschil zit hem in de functie. Maar laten we eerst onze template edit.tpl veranderen. Schrijf volgende code over in edit.tpl. edit.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
render('footer.phtml'); ?> Zoals je ziet hebben we nu waarden toegevoegd aan de Helpers en is er een action bijgekomen bij ons formulier. Nu gaan we over naar het schrijven van de editAction. Neem volgende code over, de uitleg staat zoals gewoonlijk eronder. GalleryController.php: public function editAction() { //call model $projects = new Projects();
//formulier if( $this->_request->isPost() ) { $data = array( 'titel' => $this->_request->getPost('titel') , 'omschrijving' => $this->_request>getPost('omschrijving') , 'status' => $this->_request->getPost('status') ); //data invoegen en de persoon redirecten $projects->update( $data , 'id = ' . (int) $this->_request>getParam('id') ); $this->_redirect( './gallery/' ); } else { //data ophalen $row = $projects->fetchRow('id = ' . (int) $this->_request>getParam('id') ); $this->view->paginaTitel = 'Project ' . $row->titel . ' bewerken'; $this->view->titel = 'Bewerk ' . $row->titel; $this->view->id = $row->id; $this->view->ptitel = $row->titel; $this->view->omschrijving = $row->omschrijving; } $this->render(); } code uitleg: – $projects>update( ... ): als je records wilt wijzigen moet je de update functie gebruiken. Deze heeft 2 parameters. De eerste ken je, deze is hetzelfde als voor de functie insert(). De tweede is WHERE parameter. Hij moet weten welk record er moet verandert worden. Deze ken je ook van punt 4.1. De rest van de code ken je ook al of zou je toch al moeten begrijpen. Het enige wat ons nog rest is de deleteAction.
6. Gegevens wissen We gaan eerst onze template wat veranderen omdat we een confirmatie willen. Open delete.phtml en zet volgende code erin: delete.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
titel; ?>
Verwijder project ptitel;?> ?
formButton('yes' , 'Verwijder project' , array('onClick' => "location.href='".$this>url."/gallery/delete/id/" . $this->id . "/confirm/1';") ); ?> formButton('no' , 'Annuleren' , array('onClick' => "location.href='".$this>url."/gallery/index';") ); ?>
render('footer.phtml'); ?> De template is gewijzigd en nu gaan we de code schrijven voor deleteAction(). GalleryController.php: public function deleteAction() { //call model $project = new Projects(); //delete if( $this->_request->getParam('confirm') == 1 ) { $project->delete( 'id = ' . $this->_request>getParam('id') ); $this->_redirect( './gallery/' ); } else { $row = $project->fetchRow( 'id = ' . $this->_request>getParam('id') ); $this->view->paginaTitel = 'Project verwijderen'; $this->view->titel = 'Project verwijderen'; $this->view->id = $row->id; $this->view->ptitel = $row->titel; $this->render(); } }
code uitleg: – $project>delete(...): de delete() functie zal dus een record wijzigen. Die heeft maar één parameter en dat is de WHERE. Deze ken je , dus niets nieuws meer. Verdere uitleg heeft deze code niet nodig, aangezien jij nu een bijna volleert ZF'er bent.
ZF COMPONENTEN GEBRUIKEN
Je hebt nu alle kennis om een website te maken volgens het MVC model met behulp van een framework (Zend Framework). Dit laatste deel gaat over het gebruik van andere componenten in je website. Maar eerst gaan we de contact pagina maken.
1. De contactpagina Hier gaan we al direct een component gebruiken van het framework, namelijk Zend Mail. Je kan componenten op twee manieren inladen: 1. Inladen in de bootstrap, maar dit wordt op iedere pagina ingeladen en misschien gebruik je het maar 3 keer. 2. Inladen in de action zelf, maar dan moet je het iedere keer opnieuw typen in andere actions als je het nog eens wilt gebruiken. Omdat we maar één action hebben die dit component gebruik, kiezen we voor manier 2. Open je IndexController.php en neem volgende code over. IndexController.php: public function contactAction() { $this->view->paginaTitel = 'Contact'; $this->view->titel = 'Neem contact met ons op :]'; //formulier if( $this->_request->isPost() ) { //load mail component Zend_Loader::loadClass('Zend_Mail'); $mail = new Zend_Mail(); //mail configureren $bericht = nl2br( $this->_request->getPost('bericht') ); $mail->setBodyHtml( $bericht ); $mail->setFrom( $this->_request->getPost('email') , $this>_request->getPost('naam') ); $mail->addTo( '
[email protected]' , 'Jou naam' ); $mail->setSubject('Contactpagina portfolio'); if( $mail->send() ) { $this->view->succes = true; } } //render $this->render();
} code uitleg: – new Zend_Mail(): we maken een instantie van de mail component. – setBodyHtml(): omdat we
kunnen hebben in ons bericht versturen we de mail als een HTML mail. Als je een gewone tekst mail wilt versturen vervang je setBodyHtml() in setBodyText(); – setFrom(): de geadresseerde defineren. – addTo(): de ontvanger van de mail. Je kan zoveel keer addTo() gebruiken om meerdere ontvangers toe te voegen. Je kan ook addCc() en addBcc() gebruiken. – setSubject(): het onderwerp instellen. – send(): met deze actie wordt de mail verstuurd. Ik heb de contact.phtml wat aangepast zodat je een bericht krijgt op je scherm als het bericht verstuurd is. Open contact.phtml en verander de code met die van hieronder. contact.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
titel; ?>
succes ) ) : ?>
textColor('Je mail werd succesvol verstuurd. Een reactie wordt zo snel mogelijk verstuurd.' , '#FF0000'); ?>
render('footer.phtml'); ?> Ziezo je contact pagina is nu ook gemaakt en je hebt kennis gemaakt met een component van het framework. Moeilijk? Nee!
2. Veiligheid voor alles! Zoals je wel ziet is er nergens veiligheid gebruikt. Iedereen kan HTML tags schrijven en er wordt niet gecontroleert of de velden data heeft. Daarvoor bestaat er Zend Filter en Zend Validate. We gaan maar beginnen met Zend Filter.
2.1. Zend Filter gebruiken in /gallery/add Een filter is net zoals je koffiefilter. Je hebt de input (koffie bonen) en de output is dan de koffie. We gaan de Zend Filter inladen volgens manier 1 omdat we filters meerdere malen gebruiken op een website. In /gallery/add kan je een project titel en omschrijving invullen, deze mag geen HTML tags bevatten. Daarbovenop gooien we nog eens htmlentities om mogelijke SQL injections tegen te gaan. Eerst gaan we de Filter component laden in onze bootstrap. Zet volgende code onder de laatste Zend_Loader. index.php: Zend_Loader::loadClass('Zend_Filter'); Zend_Loader::loadClass('Zend_Filter_HtmlEntities'); Zend_Loader::loadClass('Zend_Filter_StripTags'); Open nu GalleryController.php en voeg volgende code toe. GalleryController.php: public function addAction() { if( $this->_request->isPost() ) { //call model $projects = new Projects(); //call filter $filter = new Zend_Filter(); $filter->addFilter( new Zend_Filter_StripTags() ) ->addFilter( new Zend_Filter_HtmlEntities() );
$data = array( 'titel' => $filter->filter( $this->_request>getPost('titel') ) , 'omschrijving' => $filter->filter( $this>_request->getPost('omschrijving') ) , 'status' => $this->_request->getPost('status') ); //data invoegen en de persoon redirecten $projects->insert( $data ); $this->_redirect( './gallery/' ); } else { $this->view->paginaTitel = 'Project toevoegen'; $this->view->titel = 'Voeg een project toe :]'; } $this->render(); } code uitleg: – new Zend_Filter: een instantie maken van de Filter component. – addFilter( ... ): een filter toevoegen. Als je een filter toevoegt maak je een nieuwe instantie van die filter. Er moet geen ; staan na de eerste addFilter() omdat addFilter $this returnt waardoor we oneindig filters kunnen toevoegen. – filter(...): de filters worden toegepast op de ingevulde elementen. Note: gebruik htmlentites als laatste filter omdat htmlentities de andere filters kan storen. Naast HtmlEntities en StripTags zijn er nog andere standaard filters. Hier een lijstje. ● ● ● ● ● ● ● ● ● ● ● ● ●
Alnum Alpha BaseName Digits Dir HtmlEntities Input Int RealPath StringToLower StringToUpper StringTrim StripTags
De volgende code toont hoe je één filter kan toepassen. Dit is gewoon een voorbeeld. voorbeeld: filter( $input ); $htmlEntities = new Zend_Filter_HtmlEntities(); $input = $htmlEntities->filter( $input );
2.2. Zend Validate gebruiken in /gallery/add Een validator kijkt of de input voldoet aan de voorwaarden van die specifieke validator. Deze returnt altijd een boolean. Wanneer deze false is kan je kijken wat er niet aan de voorwaarden voldoet via een ingebouwde functie. Een voorbeeld met ons contact formulier. Open IndexController.php en voeg volgende code toe. IndexController.php: public function contactAction() { $this->view->paginaTitel = 'Contact'; $this->view->titel = 'Neem contact met ons op :]'; //formulier if( $this->_request->isPost() ) {
//load mail component Zend_Loader::loadClass('Zend_Mail'); $mail = new Zend_Mail(); //load email validator Zend_Loader::loadClass('Zend_Validate_EmailAddress'); $validEmail = new Zend_Validate_EmailAddress(); //mail configureren $bericht = nl2br( $this->_request->getPost('bericht') ); $mail->setBodyHtml( $bericht ); //setFrom only when email is valid! $email = $this->_request->getPost('email'); if( $validEmail->isValid( $email ) ) { $mail->setFrom( $email , $this->_request>getPost('naam') ); } else { $this->view->validError = $validEmail->getMessages(); } //mail conifgureren $mail->addTo( '
[email protected]' , 'Stijn Leenknegt' ); $mail->setSubject('Contactpagina portfolio'); if( $mail->send() ) { $this->view->succes = true; } } //render $this->render(); } code uitleg: – new Zend_Validate_EmailAddress: hiermee wordt de validator aangeroepen. Er zijn meerdere validators (zie lijst verder). – isValid(...): met deze functie controleer je of de input een emailadres is en voldoet aan de eisen van de validator. – getMessages(): wanneer deze niet voldoet komen de fouten op het scherm. (aangezien de from header niet echt nodig is om een mail te sturen moeten we de mail actie niet stopzetten). Er is een view stukje bijgekomen, dus open contact.phtml en voeg volgende code toe. contact.phtml: render('header.phtml'); ?> render('menu.phtml'); ?>
titel; ?>
succes ) ) : ?>
textColor('Je mail werd succesvol verstuurd. Een reactie wordt zo snel mogelijk verstuurd.' , '#FF0000'); ?>
validError ) ) : ?>
validError as $error ) { echo $this->textColor( $error . '
' , '#FF0000'); } ?>
render('footer.phtml'); ?> Naast de lijst met beschikbare Zend Filters is er ook een lijst met standaard Zend Validators. ● ● ● ● ●
Alnum Alpha Between Ccnum Date
● ● ● ● ● ● ● ● ● ● ● ● ● ●
Digits EmailAddress Float GreaterThan Hex Hostname InArray Int Interface Ip LessThan NotEmpty Regex StringLength
Ook zijn er weer verschillende manieren om een validator te gebruiken. Een voorbeeld. voorbeeld: addValidator('Email_Address') ->addValidator('NotEmpty'); $validator->isValid( $input );
3. Objecten registreren Dit is het laatste puntje die ik graag had uitgelegt aan jullie. Zend Framework heeft een component genaamd Zend Registry. Hierin kan je variablen opslaan met verschillende inhoud (array, strings, integers, objecten, ...). Dit is handig als je objecten aanmaakt in de bootstrap en verder wilt gebruiken in een ander bestand (bijvoorbeeld in een action). Een voorbeeld: in onze bootstrap hebben we Zend Filter ingeladen. Omdat we niet telkens een nieuwe instantie van Zend_Filter willen maken gaan we onze instantie registeren zodat we diezelfde instantie ook nog ergens anders kunnen gebruiken. Dit zou dan de index.php worden. index.php: addFilter( new Zend_Filter_StripTags() ) -> addFilter( new Zend_Filter_HtmlEntities() ); $registry->set( 'filter' , $filter ); //configure the database $options = array( 'host' => 'localhost' , 'username' => 'deb4964_mindios' , 'password' => 'Pe3rlH4rbOr' , 'dbname' => 'deb4964_mindios' ); $db = Zend_Db::factory( 'PDO_MYSQL' , $options ); Zend_Db_Table::setDefaultAdapter( $db );
//configure the front controller $controller = Zend_Controller_Front::getInstance(); $controller->setParam('noViewRenderer' , true); $controller->setControllerDirectory( './application/controllers'); //run the conroller $controller->dispatch(); code uitleg: – setInstance(): we registreren $registry zodat we die later overal kunnen oproepen. – set(...): de set functie heeft twee parameters. De eerste is de label van de variable en de tweede de inhoud. Hier is dat dus een object. Ons filter object is nu geregistreerd en nu kunnen we die makkelijk toepassen in onze addAction(). GalleryController: public function addAction() { if( $this->_request->isPost() ) { //call model $projects = new Projects(); //call filter $filter = Zend_Registry::get('filter'); $data = array( 'titel' => $filter->filter( $this->_request>getPost('titel') ) , 'omschrijving' => $filter->filter( $this>_request->getPost('omschrijving') ) , 'status' => $this->_request->getPost('status') ); //data invoegen en de persoon redirecten $projects->insert( $data ); $this->_redirect( './gallery/' ); } else { $this->view->paginaTitel = 'Project toevoegen'; $this->view->titel = 'Voeg een project toe :]'; } $this->render(); }
Met de static functie get(...) kunnen we een variable ophalen uit het register. Je kan ook het volgende doen ipv de static functie get(). //call filter $registry = Zend_Registry::getInstance(); $filter = $registry->get('filter'); Met getInstance() haal je dus het register object op die we in onze boostrap hebben geregistreerd. Het register kan heel handig zijn voor objecten te gebruiken die niet op één plaats worden gebruikt.
HET EINDE?
Dit is niet het einde van tutorials over Zend Framework maar wel voor deze reeks. Je hebt in deze laatste tutorial kennis gemaakt met andere componenten van het framework. Ik kan blijven voorbeelden geven maar ergens stopt het (auw zegt mijn hand van te copy/pasten). Er zijn nog een paar handige dingen die ik zekers met jullie deel. Het voorbeeld blijft staan op http://www.mindios.com/portfolio/ Een wereld zonder documentatie is een naieve wereld. Net zoals de manual van php.net bestaat er een manual van Zend Framework. Die manual werd veel geraadpleegt tijdens het schrijven van deze tutorials en zal nog steeds geraadpleegt worden en dat moeten jullie ook doen als je meer wilt weten over Zend Framework. Deze kan je vinden op volgende link. http://framework.zend.com/wiki/display/DOCDEVNL/Home Naast de manual is er ook nog veel support beschikbaar. Er zijn de ZF forums en #zftalk (FreeNode) waar je altijd terecht kan met je vragen over ZF. http://www.sitemasters.be/?pagina=tutorials/tutorials&cat=4&id=455 , deze tutorial is geschreven door de persoon die mij in contact heeft gebracht met ZF en ook met OOP. Hierin legt hij het gebruik uit twee belangrijke Zend Framework componenten, namelijk Zend Cache en Zend Config. Twee belangrijke componenten. http://zendext.maaksite.nl/Main_Page , is een site die meehelpt om het framework te verbeteren en uit te breiden. Vragen of opmerkingen >
[email protected] Stijn Leenknegt Tot de volgende!