maandag 23 juni 2008

Deel 6: gebruikers autoriseren

Apache werkt, php werkt, Cake werkt, database klaar, daar gaat ie dan.
- Maak een werkdirectory onder je devel directory, genaamd scolafavs
- Maak een virtual host aan in C:\devel\EasyPHP\apache\conf\extra\httpd-vhosts.conf en in c:\windows\system32\drivers\etc\hosts
- herstart apache
- ga in devel\scolafavs staan en type
cake bake
creëer hiermee je applicatie skeleton en database connectie script. Hierna moet je via http://scolafavs/ de maagdelijke applicatie op kunnen vragen, en wel met allemaal groene balken (geen foutmeldingen).

Erg mooi vind ik die pagina niet, maar wel heel functioneel. Laten we eens naar de opmaak kijken. In de directory views/layouts staat het bestand default.ctp. Een ctp bestand in een cake template, hier wordt bepaald hoe de pagina's (die van deze layout gebruik maken) eruit komen te zien. Je kunt de pagina openen in een wysiwyg editor zoals macromedia, sorry adobe dreamweaver, of gratis: microsoft visual webdeveloper express ed en zo zijn er nog wel een paar. Maak er een leuke layout van. Bij mij zien head en body er even zo uit:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head>
<?php echo $html->charset(); ?>
<title>
ScolaFavs
</title>
<?php
echo $html->css('stijlen');
echo $scripts_for_layout;
?>
</head>
<body>
<div id="container">
<div id="content">
<?php
if ($session->check('Message.flash')):
$session->flash();
endif;
echo $content_for_layout;
?>
</div>
</div>
<?php echo $cakeDebug?>
</body>
</html>


Ik heb er een externe css voor gemaakt die geplaatst is in webroot\css. Laat de variabele $cakeDebug en Message.flash vooral staan, ze kunnen je helpen. In productie verhoog je het debuglevel, zodat foutmeldingen niet doorkomen naar de eindgebruiker. In $content_for_layout komt de content te staan die een view produceert.

De applicatie moet gebruikers herkennen, daarom hebben we een inlogsysteem nodig.
Als basis gebruik ik dit artikel uit de bakery, de CakePHP documentatie.
De users tabel gaat de users van de applicatie herbergen (duhhh). Ik moet nog steeds wennen aan wat nu precies in enkelvoud en wat in meervoud moet (en dat is wel zo handig om ook zo te doen!), daarom heb ik er een klein schemaatje van gemaakt:


Wat hebben we concreet nodig: er is een users tabel (meervoud) die wordt benaderd via een user model (enkelvoud) die wordt aangestuurd door een users_controller die weer naar de bezoeker gaat via views/users/index.ctp of login.ctp als nog niet is ingelogd (de dirname is meervoud). confused? Toch is het prettig als je dit gewoon zo doet dat cake zonder meer weet waar alles staat en hoe alles heet zodra je de users controller benadert.

Een users tabel hebben we al, daar moet dus een model omheen geplaatst worden om de toegang naar de database te regelen. We laten Cake zelf het model bakken.

Open nu het bestand models/user.php en pas het als volgt aan:
<?php 
class User extends AppModel {
var $name = 'User';

function validateLogin($data) {
$user = $this->find(array('un' => $data['username'],
'pw' => md5($data['password'])),
array('id', 'un'));
if(!empty($user)) {
return $user['User'];
}
return false;
}
}
?>

Een opmerking hierbij: Het is niet altijd duidelijk of een dergelijke functionaliteit nu in de controller of in het model moet worden geregeld. Ik voel wel voor deze oplossing omdat er alleen een databasequery plaatsvindt. De $data wordt doorgegeven door de controller die als enige iets met POST en GET requests te maken heeft. Het valideren van een login is volgens een andere leerschool echter iets voor in de controller.

Nu volgt dan de controller:
Dit keer zullen we hem zelf maken. Creëer een nieuw bestand in de controllers directory dat je users_controller.php noemt. Het bestand krijgt de volgende inhoud:
<?php
class UsersController extends AppController {
var $name = "Users";
var $helpers = array('Html', 'Form');

function index() {}

function beforeFilter() {
$this->__validateLoginStatus();
}

function login() {
if(!empty($this->data)) {
if(($user = $this->User->validateLogin($this->data['User'])) == true) {
$this->Session->write('User', $user);
$this->Session->setFlash('Je bent ingelogd.');
$this->redirect('index');
exit();
} else {
$this->Session->setFlash('De inloginformatie is niet correct.');
$this->redirect('login');
exit();
}
}
}

function logout(){
$this->Session->destroy('user');
$this->Session->setFlash('Je bent uitgelogd.');
$this->redirect('login');
}

function __validateLoginStatus(){
if($this->action != 'login' && $this->action != 'logout'){
if(!$this->Session->check('User')){
$this->Session->setFlash('Om deze pagina te bekijken moet je eerst inloggen');
$this->redirect('login');
}
}
}
}
?>

Een paar opmerkingen hierbij.
De method beforeFilter() wordt automatisch uitgevoerd als je een controllermethod aanroept, welke dan ook. Dit is dus een goede plaats om te zorgen dat de autorisatie plaatsvindt. De autorisatie zelf komt in een functie __validateLoginStatus die begint met __ om 'm 'private' te maken. Het valideren van de inloggegevens gebeurt door de zojuist gemaakte functie in het model aan te roepen. Uitloggen is een kwestie van de sessie opruimen. Andere pagina's die beveiligd zijn kunnen gebruikmaken van dezelfde check. Over het algemeen zullen alle controlleractions of beveiligd, of niet beveiligd zijn.

Rest nog de views, 1 voor het inlogscherm en 1 voor als het inloggen gelukt is:
Maak twee bestanden in views\users: index.ctp en login.ctp
Hierin staan dus geen <html> enzo, dat staat in layouts/default.ctp. Het volgende staat er wel in:


views\users\login.ctp
<div class="login">
<h2>Login</h2>
<?php echo $form->create('User', array('action' => 'login'));?>
<table>
<tr>
<td>
<?php
echo $form->input('username', array(
'label'=>'username:</td><td>'
));
?>
</td>
</tr>
<tr>
<td>
<?php
echo $form->input('password', array(
'label'=>'password:</td><td>'
));
?>
</td>
</tr>
<tr>
<td colspan='2' align='right'>
<?php echo $form->submit('-login-');?>
</td>
</tr>
</table>
<?php echo $form->end(); ?>
</div>

views\users\index.ctp

<?php echo $html->link('Logout', array('controller' => 'Users', 'action' => 'logout')); ?>
<br/><br/>
You've accessed the secret secure location!


Het loginschermpje wilde ik netjes opmaken, waardoor een beetje extra code nodig was tov het eerder genoemde artikel.

Tenslotte is natuurlijk ook wat databasevulling nodig (initiele gebruikersaccounts, een paar links en een paar groepen) die je hier kunt downloaden. Om de controller starten: http://scolafavs/users/
De controller zal de index starten, maar daarvoor de validatie uitvoeren. Omdat er nog niet is ingelogd wordt geforward naar de loginpagina. Nu kun je inloggen en de index wordt getoond.

De volledige code staat hier

vrijdag 13 juni 2008

Deel 5: de database voor ScolaFavs

Om verder te gaan heb ik een applicatie in mijn hoofd waarmee gebruikers links in groepen kunnen plaatsen en zo altijd het setje favorieten bij zich hebben. Verder (later) moet het mogelijk worden een pagina met link-groepen te kunnen delen onder users. Ik noem deze applicatie ScolaFavs.

Mysql Workbench
Ik heb voor het ontwerp van de database maar eens gekozen voor open source tool: MySql Workbench. Ik moet zeggen: een erg mooie tool. (let op: op dit moment alleen nog onder windows te krijgen, voor linux kun je DBDesigner 4 gebruiken (eveneens open source), die blijft beschikbaar totdat workbench ook voor linux het levenslicht ziet).
Na installeren en opstarten verschijnt het hoofdscherm met een lege database erin die mydb heet. Dat lijkt niet wat we willen dus rechtsklikken op het databasesymbool en kies edit schema.

Ik heb de database scolafavs genoemd. Nu we hier toch zijn kunnen we gelijk de charset van de database op utf8 zetten (utf8-genral-ci). Nu gaan we het ERD schema zelf tekenen. Dubbelklik op add diagram. Er wordt een nieuw scherm geopend om het schema op te tekenen. Klik nu op het icoontje dat eruit ziet als een tabelletje (Place a new table). De muiscursor verandert nu als je boven het werkplaat beweegt, in een handje met een tabelletje. Plaats de tabel nu door op het werkblad te klikken. Door op het vlakje met table1, de lege tabel, te dubbelklikken opent zich onderin het scherm een serie tabbladen waarmee de naam van de tabel kan worden ingevoerd en de velden in de tabel. Vul nog niet de relaties-velden (FK) in, dat gaat straks automatisch.
De tabellen zien er nu zo uit:

links(id, naam, url)
groups(id, naam)
users(id, naam, un, pw)

id is steeds int, PK en autonumber (checkbox AI) en dus ook not null (checkbox NN)
Let op: de tabelnamen zijn in meervoud, omdat CakePHP ze zo verwacht en dan weet hoe het model moet worden gemaakt.

Nu de FK relaties. Eerst gaan we controleren hoe de gegenereerde FK-velden zullen worden genoemd. Opnieuw, om optimaal van de active recordset van CakePHP gebruik te maken moeten de foreign keys van de vorm ..._id zijn (waarbij op de ... de tabelnaam verschijnt waarmee gelinkt wordt) in plaats van id_.... Tevens moet de primary key van de tabellen id heten.

Om zeker te weten dat dit naar wens gaat moet je de settings bekijken (tools > options).

Een user heeft n groepen en die bestaan weer uit n links.
Kies "place a new 1..n non-identifying relationschip" (icoontje 6e van onder, identifying is voor gedeeltelijke relatie in een n:m tabel). Kijk in de statusbalk (please select the table to receive the foreign key). Klik op de 'links' tabel, in de statusbalk verschijnt nu select the referenced table. Klik op de 'groups' tabel. De relatie wordt getekend. Zo kan de tweede relatie tussen 'groups' en 'users' ook getekend worden. Het model ziet er nu zo uit:

Om dit model om te zetten in een .sql bestand, kies File > export > forward engineer CREATE script. In de popup: Generate DROP TABLE en kies een outputnaam (bijv. D:\scolafavs.sql) > next > finish
Nu heb je een mysql-script (.sql) waarmee je de database kunt vullen. Je kunt hiervoor phpmyadmin gebruiken. Je hoeft niet eerst een database aanmaken, er wordt een gemaakt die scolafavs heet, als je tenminste de database zo genoemd hebt (bij edit schema, hierboven). We zijn klaar om terug te gaan naar CakePHP!

Het complete Mysql Workbench bestand is hier te downloaden, maar zelf maken is natuurlijk leuker!

dinsdag 10 juni 2008

Deel 4: Controller en View, je eerste applicatie

Na de M komen de C en de V

De controller: ga naar devel\projects\social\3_bouw en type cake bake
Kies nu c en volg de voorbeelden op het scherm.


De v gaat in een moeite door. Hierna heb je eencomplete applicatie, maar niet "productierijp".


De applicatie kan opgevraagd worden via een webbrowser:

maandag 9 juni 2008

Deel 3: bake - het model

Voor het voorbeeld volg ik vanaf nu het eerder genoemde boek van Duane o'Brien.
Om code te genereren (bake) wordt gebruik gemaakt van de CLI versie van php, die een eigen php.ini heeft. Als deze ini niet bestaat, staat een heleboel niet aan, waaronder mysql. Je kunt php -i gebruiken om te zien wat er allemaal aan settings aanwezig is (vergelijkbaar met phpinfo() maar dan op de commandline).
Bekijk of mysql beschikbaar is voor de PHP CLI.
php -m
en controleer of mysql erbij staat. Als dat niet zo is gaat bake van het model niet lukken, en krijg je de foutmelding:
Call to undefined function mysql_connect()
met nog het een en ander eromheen.
Bake werkt (vooralsnog) met php cli, de commandline versie van PHP. Deze CLI heeft een eigen php.ini.

Eerst moet je weten waar PHP CLI zijn settings zoekt:

php -i > info.txt

Voor het gemak sturen we de output naar info.txt. Open nu info.txt en zoek naar de regel die begint met Configuration File (bovenin)

In mijn geval wordt php.ini onder \windows gezocht. In die dir bleek bij mij helemaal geen php.ini te staan.
Kopieer uit de EasyPHP/php5 dir het bestand php.ini-dist naar de dir waar php-cli hem zoekt en hernoem het naar php.ini. Open het bestand en zoek de regel extension=php_mysql.dll
Deze regel is waarschijnlijk uitgecommentarieerd (een ';' als eerste teken). Haal de ; weg aan het begin van de regel. Na opslaan kun je opnieuw php -m uitvoeren op de commandline.

Als mysql er nu wel bij staat kun je de volgende stap maken: het bakken van een model. Daar heb je eerst een databaseschema voor nodig. Maak een database aan in mysql
create database social

en voeg de volgende tabel toe:

CREATE TABLE `users` (
`id` int(10) NOT NULL auto_increment,
`firstname` varchar(40) NOT NULL,
`lastname` varchar(40) NOT NULL,
`screenname` varchar(40) NOT NULL,
`email` varchar(255) NOT NULL,
`password` varchar(40) NOT NULL,
`created` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM;


Ga nu in de dir .../projects/social/3_bouw staan en type
cake bake
De bake applicatie ziet dat het skeleton op zijn plaats staat en dat de database connectie in orde is. kies M om een model te maken.
Volg de aanwijzingen in de screenshot.

Als resultaat kun je de enorm complexe code bekijken die in de model dir is gegenereerd:



OK, niet echt indrukwekkend maar dat komt nog...

woensdag 4 juni 2008

Deel 2: De eerste stappen

Als intro heb ik gebruik gemaakt van dit sitepoint artikel. Als je al eerder met MVC frameworks hebt gewerkt, kun je de eerste bladzijde overslaan. De tweede begint met de scaffolding mogelijkheden wat op de derde weer wordt herschreven. Uiteindelijk heb je iets van helpers en iets van templates gezien. Genoeg om de duik te wagen: ga een boek zoeken (op dit moment zijn die er niet, deze is in de maak) en duik de documentatie op de website in.

De meeste tutorials gaan uit van de app dir die onder cake staat. De setup die hiervoor staat is echter beter: per project een eigen project dir met daarin het app gedeelte van het framework. Dit betekent dat je de meeste tutorials iets moet aanpassen, maar als je dat eenmaal weet, werkt het wel zo prettig.

We gaan uit van een applicatie die ik social zal noemen. Om te beginnen moet de directory-structuur voor de applicatie worden aangemaakt en de initiële framework bestanden. Start een dosbox en type:
cake bake<enter>

en volg verder het voorbeeld:

Bij mij lukt het niet om in een moeite door het database connectiescript te genereren. daarom sluit ik cake, cd naar 3_bouw en start cake opnieuw:

Nu lukt het wel. Onder ../projects zie je nu een dir social/3_bouw waar het Cake "skeleton" in staat die je applicatie gaat worden.


Om de applicatie te starten maak ik een vhost aan (http://social in plaats van http://localhost/social):

  • pas devel\EasyPHP\apache\conf\extra\httpd-vhosts.conf aan, voeg toe:

    <VirtualHost *:80>
    DocumentRoot C:/devel/projects/social/3_bouw/webroot
    ServerName social:80
    ErrorLog logs/social_error.log
    CustomLog logs/social_access.log common
    </VirtualHost>


  • pas aan: C:\WINDOWS\system32\drivers\etc\hosts, voeg toe:

    127.0.0.1 social

  • Herstart apache


Als alles goed is gegaan kun je nu met de volgende URL het standaard Cake scherm zien
http://social/

De opmerking over het afwezig zijn van een databasefile gaan we in deel 3 oplossen.

Deel 1: Installatie van Cake

Dit stukje is gebaseerd op dit stukje.
Ik heb een aantal zaken aangepast. Het zou met alleen deze info moeten lukken:
Ik vond easyphp een prima alternatief voor xampp, er zit alleen apache, php, mysql en phpmyadmin in en laat zich heel eenvoudig installeren. Ik neem als voorbeeld een applicatie die ik social noem, vrij naar het boek van Duanne o'Brien.

Ter referentie: de uiteindelijke TREE ziet er zo uit:

devel
    \_ EasyPHP
    \_ projects
          \_ cake
                \_console (zie *)
          \_ social
                \_ 1_vooronderzoek
                \_ 2_ontwerp
                \_ 3_bouw
                \_ 4_deploy


1. Creëer de devel directory
2. Creëer daaronder een projects directory
3. Download easyphp: http://www.easyphp.org/ en installeer de laatste versie. Installatiedir is devel\EasyPHP
Bewonder het trayicon die direct toegang geeft tot de configuratie en de logs van MySql en Apache. Ik moest overigens de tool helemaal afsluiten en weer opstarten voor Apache en Mysql het deden. Dit gebeurde alleen de eerste keer. Persoonlijk had ik xampp al geinstalleerd, maar ik heb er geen spijt van dat ik die heb verwijderd, EasyPHP is gemakkelijker en bevat geen extra's die ik niet gebruik.

4. Download cakephp: http://www.cakephp.org/. Pak het archief bestand uit in devel\projects\cake. Zorg dat je versie 1.2 download (is op het moment van dit schrijven in beta)

5. Pas de environment variabele (control panel - systeem - advanced - environment variables - user variableas) PATH aan door deze uit te breiden met:
C:\devel\EasyPHP\php5\;
C:\devel\EasyPHP\php5\ext\;
C:\devel\EasyPHP\mysql\bin;
C:\devel\projects\cake\cake\console;

6. Pas httpd.conf aan:
  • Wijzig
    DocumentRoot "${path}\www"
    in
    DocumentRoot "C:\devel\projects"
    en
    <Directory "${path}/www>
    in
    <Directory "C:\devel\projects">

  • Zorg dat AllowOverride op All staat voor deze directory

  • Zorg dat deze regel niet uitgecommentarieerd is:
    LoadModule rewrite_module modules/mod_rewrite.so

  • Om met vhosts te gaan werken (daar ga ik vanuit) moet je het commentaar # in de regel
    Include conf/extra/httpd-vhosts.conf
    weghalen.


  • (*) Als cake\cake\console niet bestaat heb je mogelijk de verkeerde versie van Cake geinstalleerd. type cake in een commandbox om de versie te bekijken.