Vous êtes sur la page 1sur 13

Tutoriel CakePHP

Crez rapidement vos applications avec CakePHP

Auteur

Date de dernire version 18/04/11

Numro de version 1.2

Christian Buffin Formation CakePHP 1.3.8

Page 1 / 13

Rvisions SQL
1.Trouver tous les posts qui sont visibles
SELECT * FROM posts WHERE visible = true;

2.Trouver tous les posts qui sont visibles et trier le jeu de rsultats par date de cration en ordre descendant.
SELECT * FROM posts WHERE visible = true ORDER BY created DESC;

3.Ajouter l'utilisateur li au jeu de rsultats.


SELECT * FROM posts INNER JOIN users ON ( posts.user_id = users.id ) WHERE posts.visible = true ORDER BY posts.created DESC;

4.a. PostgreSQL - Trouver l'id et le title de 2 posts, aliaser le nom de la table (par Post) et prfixer le nom de chaque colonne par Post__
SELECT "Post"."id" AS "Post__id", "Post"."title" AS "Post__title" FROM "posts" AS "Post" LEFT JOIN "users" AS "User" ON ("Post"."user_id" = "User"."id") LIMIT 2

4.b. MySQL - Trouver l'id et le title de 2 posts


SELECT posts.id, posts.title FROM posts LEFT JOIN users ON (posts.user_id = users.id) LIMIT 2

Programmation oriente objet


1. Crez une classe Animal qui possde un attribut publique nom (dont la valeur par dfaut est Animal ) et une mthode publique manger qui affiche <nom> mange .
class Animal { public $nom = 'Animal'; public function manger() { echo "{$this->nom} mange"; }

Formation CakePHP 1.3.8

Page 2 / 13

2. Crez une classe Chien qui hrite de la classe Animal et qui surcharge l'attribut name en lui donnant la valeur par dfaut Mdor .
class Chien extends Animal { public $nom = 'Mdor'; }

3. Compltez la classe prcdente en surchargeant la mthode manger pour qu'elle affiche ce qu'affiche la classe Animal, plus une ligne qui affiche <nom> est un bon chien .
class Chien extends Animal { public $nom = 'Mdor'; public function manger() { parent::manger(); echo "{$this->nom} est un bon chien"; }

4. Modifiez la classe prcdente pour ne pas afficher le rsultat de la mthode manger de la classe Animal. la place, affichez <nom> est un bon chien qui mange .
class Chien extends Animal { public $nom = 'Mdor'; public function manger() { echo "{$this->nom} est un bon chien qui mange"; }

Formation CakePHP 1.3.8

Page 3 / 13

Code gnr par la console Localisation


Crez et remplissez le fichier de traduction en franais pour le code gnr par la console (seulement pour la partie posts/edit.ctp). Testez dans votre navigateur.
# app/locale/fre/LC_MESSAGES/default.po msgid "Invalid post" msgstr "Billet invalide" msgid "The post has been saved" msgstr "Impossible d'enregistrer le billet" msgid "The post could not be saved. Please, try again." msgstr "Impossible d'enregistrer le billet. Veuillez ressayer"

Paramtres
Essayez de dboguer les paramtres qui sont passs au contrleur dans les mthodes index, view et edit du contrleur PostsController.
debug( $this->params['pass'] ); // Paramtres GET, faon CakePHP

debug( $this->data ); // Paramtres POST, commenter le redirect pour les voir.

debug( $this->params['named'] ); // Paramtres nomms

Pages d'erreur
Modifiez le code de la mthode view de la classe PostsController pour envoyer une page 404 lorsque l'enregistrement n'est pas trouv.
function view($id = null) { $post = $this->Post->read(null, $id); if( empty( $post ) ) { $this->cakeError( 'error404' ); } $this->set('post', $post);

Formation CakePHP 1.3.8

Page 4 / 13

Add / edit
Regroupez les mthodes add et edit de PostsController ainsi que les vues associes, afin de passer par la mthode _add_edit. PostsController
function add() { $args = func_get_args(); call_user_func_array( array( $this, '_add_edit' ), $args ); } function edit() { $args = func_get_args(); call_user_func_array( array( $this, '_add_edit' ), $args ); } function _add_edit( $id = null ) { if( $this->action == 'edit' ) { $post = $this->Post->read( null, $id ); if( empty( $post ) ) { $this->cakeError( 'error404' ); } } if (!empty($this->data)) { $this->Post->create(); if ($this->Post->save($this->data)) { $this->Session->setFlash(__('The Post has been saved', true)); $this->redirect(array('action' => 'index')); } else { $this->Session->setFlash(__('The Post could not be saved. Please, try again.', true)); } } if( $this>action == 'edit' && empty( $this->data ) ) { $this->data = $post; } $this->render( null, null, '_add_edit' ); }

_add_edit.ctp
<div class="posts form"> <?php echo $this->Form->create('Post');?> <fieldset> <legend><?php

Formation CakePHP 1.3.8

Page 5 / 13

<?php

if( $this->action == 'add' ) { __('Add Post'); } else { __('Edit Post'); } ?></legend> if( $this->action == 'edit' ) { echo $this->Form->input('id'); } echo $this->Form->input('title'); echo $this->Form->input('user_id'); echo $this->Form->input('visible'); echo $this->Form->input('introduction'); echo $this->Form->input('content'); echo $this->Form->input('Tag');

?> </fieldset> <?php echo $this->Form->end(__('Submit', true));?> </div> <div class="actions"> <h3><?php __('Actions'); ?></h3> <ul> <?php if( $this->action == 'edit' ):?> <li><?php echo $this->Html->link(__('Delete', true), array('action' => 'delete', $this->Form->value('Post.id')), null, sprintf(__('Are you sure you want to delete # %s?', true), $this->Form>value('Post.id'))); ?></li> <?php endif;?> <li><?php echo $this->Html->link(__('List Posts', true), array('action' => 'index'));?></li> </ul> </div>

Formation CakePHP 1.3.8

Page 6 / 13

Modle
Dans le modle Post, arrangez-vous pour que l'on puisse supprimer un billet mme si son id est cl trangre pour d'autres modles.
public $hasMany = array( 'Comment' => array( 'dependent' => true ) );

Dans le contrleur Posts, mthode view, dboguez le nombre d'utilisateurs dont l'id est strictement suprieur 2.
$count = $this->Post->User->find( 'count', array( 'conditions' => array( 'User.id >' => 2 ) ) ); debug( $count );

Formation CakePHP 1.3.8

Page 7 / 13

Vue
1. Dans la vue index lie au contrleur Posts, changez le colonne user_id en User.username. Au besoin, modifiez la rcursivit dans la mthode du contrleur. Vrifiez que l'on puisse bien trier le tableau de rsultats par nom d'utilisateur. views/posts/index.ctp
<th><?php echo $paginator->sort('title');?></th> <th><?php echo $paginator->sort('User.username');?></th> <th><?php echo $paginator->sort('introduction');?></th> <?php echo $post['Post']['title']; ?> </td> <td> <?php echo $post['User']['username']; ?> </td> <td> <?php echo $post['Post']['introduction']; ?>

controllers/posts_controller.php
function index() { $this->Post->recursive = 0; $this->set('posts', $this->paginate()); }

2. Recopiez le layout par dfaut se trouvant dans cake/libs/views/layout/default.ctp dans app/views/layouts/default.ctp. Ajoutez des liens (en utilisant la mthode link du helper Html) vers les pages d'index de tous vos contrleurs.
<div id="menu"> <?php echo $this->Html->link( 'Billets', array( 'controller' => 'posts', 'action' => 'index' ) ); ?> <?php echo $this->Html->link( 'Utilisateurs', array( 'controller' => 'users' ) ); ?> // ... </div>

3. Modifiez la mthode _add_edit du contrleur Posts, la vue associe et le modle associ pour obtenir une liste droulante des utilisateurs pour le champ Post.user_id. Vous utiliserez la mthode find( 'list' ). controllers/posts_controller.php
// ... $listUsers = $this->Post->User->find( 'list' ); $this->set( 'listUsers', $listUsers ); $this->render( null, null, '_add_edit' );

Formation CakePHP 1.3.8

Page 8 / 13

models/user.php
<?php class User extends AppModel { var $name = 'User'; var $displayField = 'username'; // ...

views/posts/_add_edit.ctp
echo $this->Form->input( 'user_id', array( 'options' => $listUsers ) );

Remarque: on n'est pas obligs de de spcifier les options utiliser pour le champ user_id, puisqu'on a utilis la variable $users (et que notre champ s'appelle user_id) magie de CakePHP. Je vous conseille donc d'tre explicites pour viter de mauvaises surprises. Nanmoins, voici le code permettant d'utiliser la magie de CakePHP. controllers/posts_controller.php
// ... $users = $this->Post->User->find( 'list' ); $this->set( 'users', $users ); $this->render( null, null, '_add_edit' ); }

views/posts/_add_edit.ctp
echo $this->Form->input( 'user_id' );

L'explication de cette magie est que l'on a bien respect les conventions CakePHP, et que l'on a nomm le champ user_id (nom de la table au singulier, underscore id) puisque ce champ correspond une cl trangre. 4. Modifiez le code prcdent pour obtenir par dfaut une entre vide dans la liste droulante. Aidez-vous du CookBook en regardant l'aide pour la mthode input du FormHelper.
echo $this->Form->input( 'user_id', array( 'empty' => true ) );

5. Modifiez la mthode _add_edit du contrleur Posts et la vue associe pour obtenir des cases cocher des divers tags que l'on peut associer au billet. Vrifiez que l'enregistrement s'effectue correctement. controllers/posts_controller.php

Formation CakePHP 1.3.8

Page 9 / 13

// ... $this->set( 'tags', $this->Post->Tag->find( 'list' ) ); $this->render( null, null, '_add_edit' ); }

views/posts/_add_edit.ctp
echo $this->Form->input( 'Tag', array( 'multiple' => 'checkbox' ) );

5. Dans l'index des billets, ajoutez une colonne contenant la liste des tags, utilisez la classe Set. N'oubliez pas d'augmenter la rcursivit. controllers/posts_controller.php $this->Post->recursive = 1; $this->set('posts', $this->paginate()); views/posts/index.ctp
<td><?php echo $post['Post']['visible']; ?>&nbsp;</td> <td><?php echo implode( ', ', Set::extract( $post, '/Tag/name' ) >&nbsp;</td> <td><?php echo $post['Post']['introduction']; ?>&nbsp;</td>

); ?

6. Dans la vue de la mthode view du contrleur Posts, prsentez les donnes comme pour un vrai blog (Titre, auteur / date de cration, tags, introduction, contenu, commentaires). Proccupezvous uniquement du HTML (pas du CSS). Utilisez les fonctions PHP strftime et strtotime pour obtenir la date dans la langue dfinie par setlocale. Inspirez-vous d'un billet du site http://www.zeldman.com/ views/posts/view.ctp
<div class="posts view"> <h1><?php echo $post['Post']['title'];?></h1> <p>Cre par <?php echo $post['User']['username'];?> le <?php echo strftime( '%A %e %B %Y %H:%M', strtotime( $post['Post']['created'] ) );? >.</p> <?php $tags = Set::combine( $post, 'Tag.{n}.id', 'Tag.{n}.name' ); if( empty( $tags ) ) { echo $this->Html->tag( 'p', 'Non catgoris' ); } else { $liens = array(); foreach( $tags as $id => $name ) { $liens[] = $this->Html->link( $name, array( 'controller' => 'tags', 'action' => 'view', $id ) ); } echo $this->Html->tag( 'p', 'Catgoris dans: '.implode( ',

Formation CakePHP 1.3.8

Page 10 / 13

', $liens ) ); } ?> <div class="introduction"> <?php echo $post['Post']['introduction'];?> </div> <div class="content"> <?php echo $post['Post']['content'];?> </div> <div class="comments"> <h2>Commentaires</h2> <?php if( empty( $post['Comment'] ) ) { echo $this->Html->tag( 'p', 'Aucun commentaire' ); } else { foreach( $post['Comment'] as $comment ) { $tmp = ''; $tmp .= $this->Html->tag( 'h3', $comment['name'] ); $tmp .= $this->Html->tag( 'p', strftime( 'Cre le %A %e %B %Y %H:%M', strtotime( $comment['created'] ) ) ); $tmp .= $this->Html->tag( 'div', $comment['content'] ); echo $this->Html->tag( 'div', $tmp, array( 'class' => 'comment' ) ); } } ?> </div> </div>

7. Faites un formulaire de recherche pour la mthode index du contrleur Posts. Vous afficherez 4 champs dans le formulaire: Post.visible , Post.user_id, Post.title et Post.created. Construisez votre moteur champ par champ ( d'abord Post.visible, puis Post.visible et Post.user_id, etc... ). Utilisez (du moins terme) la mthode postConditions de la classe Controller. models/post.php
class Post extends AppModel { public $name = 'Post'; public $order = array( 'Post.id ASC' ); // ... }

Formation CakePHP 1.3.8

Page 11 / 13

views/posts/index.ctp ( rajouter avant le tableau de rsultats)


<?php /*

Il faut prciser que l'on retourne bien sur la mthode index du contrleur Posts. Par dfaut, CakePHP renverra vers la mthode add du contrleur courant. */ echo $this->Form->create( null, array( 'url' => array( 'controller' => 'posts', 'action' => 'index' ) ) ); echo $this->Form->input( 'Post.user_id', array( 'empty' => true ) ); echo $this->Form->input( 'Post.title' ); /* Le champ created est de type timestamp/datetime, on va le forcer tre de type date, avec un format francophone et une anne maximale correspondant l'anne en cours */ echo $this->Form->input( 'Post.created', array( 'type' => 'date', 'dateFormat' => 'DMY', 'empty' => true, 'maxYear' => date( 'Y' ) ) ); echo $this->Form->input( 'Post.visible' ); echo $this->Form->end( __( 'Search', true ) ); ?>

controllers/posts_controller.php
public function index() { $conditions = array(); $data = $this->data; if( !empty( $this->data ) ) { // Nettoyage des champs vides foreach( array( 'Post.user_id', 'Post.title' ) as $path ) { $value = Set::classicExtract( $data, $path ); if( empty( $value ) ) { $data = Set::remove( $data, $path ); } } // Variable temporaire pour stocker la date de cration $created = $data['Post']['created']; $data = Set::remove( $data, 'Post.created' ); // Transformation en conditions $conditions = $this->postConditions( $data, array( 'Post.title' => 'LIKE' ) ); // Ajout ventuel du critre sur la date de cration (pour le type timestamp/datetime de la BDD) if( !empty( $created['day'] ) && !empty( $created['month'] ) && ! empty( $created['year'] ) ) { $createdStart = "{$created['year']}-{$created['month']}{$created['day']}";

Formation CakePHP 1.3.8

Page 12 / 13

$createdStop = date( 'Y-m-d', strtotime( $createdStart ) + ( 24 * 60 * 60 ) ); $conditions[] = "Post.created BETWEEN '{$createdStart}' AND '{$createdStop}'"; } } $this->Post->recursive = 0; $this->set( 'posts', $this->paginate( null, $conditions ) ); $this->set( 'users', $this->Post->User->find( 'list' ) ); }

controllers/posts_controller.php (mthode alternative)


public function index() { $conditions = array(); $data = $this->data; if( !empty( $this->data ) ) { // Variable temporaire pour stocker la date de cration $created = $data['Post']['created']; $data = Set::remove( $data, 'Post.created' ); // Transformation en conditions $conditions = $this->postConditions( $data, array( 'Post.title' => 'LIKE', 'Post.user_id' => 'LIKE' ) ); // Ajout ventuel du critre sur la date de cration (pour le type timestamp/datetime de la BDD) if( !empty( $created['day'] ) && !empty( $created['month'] ) && ! empty( $created['year'] ) ) { $createdStart = "{$created['year']}-{$created['month']}{$created['day']}"; $createdStop = date( 'Y-m-d', strtotime( $createdStart ) + ( 24 * 60 * 60 ) ); $conditions[] = "Post.created BETWEEN '{$createdStart}' AND '{$createdStop}'"; } } $this->Post->recursive = 0; $this->set( 'posts', $this->paginate( null, $conditions ) ); $this->set( 'users', $this->Post->User->find( 'list' ) ); }

Formation CakePHP 1.3.8

Page 13 / 13