Maintenant que nous commençons à connaître symfony

Publié par Aline Bunelle Ven 21 déc 2007 17:06:00 GMT

Nous avons déjà vu pas mal de chose sur symfony, et nous commençons maintenant à nous y habituer.

Allons plus loin dans la configuration

Nous n’avons pour l’instant que la configuration d’une route par défaut, et de notre layout. Mais comme vous vous en doutez il y a beaucoup d’autres choses plus intéressantes que l’on peut configurer.

Factories

Le fichier factories.yml défini les classes à utiliser pour le fonctionnement des :
  • controller // Gestion des actions
  • request // Gestion des requêtes du client
  • response // Gestion des réponses du client
  • user // Gestion du compte utilisateur
  • storage // Gestion de la session
  • cache // Gestion du cache
Il sera rare de venir modifier les classes utilisées par défaut, hormis changer le nom du cookie :
all:
  storage:
    class: sfSessionStorage
    param:
      session_name: NOM_COOKIE

Ou de modifier le nom de la classe myUser (créer par défaut) sans grand intérêt.

Si vous redéfinissez une classe elle doit évidemment se situer dans le dossier lib de l’application, ou à la racine si elle s’applique à toutes vos applications.

Settings

Le fichier settings.yml va nous permettre de définir beaucoup de choses. Consultez ce fichier, il contient toutes les posibilités commentées. Ces valeurs commentées sont en réalité les valeurs par défaut affectées à votre application.

Vous pourrez entre autre gérer les modules et actions à appeler en cas de diverses erreurs.

Nous avons vu les validations et l’affichage d’erreurs (dans les formulaires). Vous avez donc remarqué l’affichage de fléches vers le bas encadrant ces messages. Vous pouvez modifier ces caractères de préfixe et suffixe ici également :
#    validation_error_prefix:    ' ↓ '
#    validation_error_suffix:    '  ↓'
Ou encore faire appel à d’autre fonction, ou paramétrer l’id des balises « span » des erreurs :
#    validation_error_class:     form_error
#    validation_error_id_prefix: error_for_
Cette ligne permet de définir le nombre maximum de `forward` que l’application peut suivre avant de déclencher une exception (5 par défaut) :
#    max_forwards:           5

Chaque paramètre est commenté, je vous laisse donc lire ce fichier.

Publié sous  | Mots clés  | aucun commentaires

Symfony et Validator (suite et fin)

Publié par Aline Bunelle Ven 21 déc 2007 17:03:00 GMT

Les Validator existants

Nous avons vu rapidement le validator sfPropelUniqueValidator mais il en existe d’autre :
  • sfStringValidator
  • sfNumberValidator
  • sfEmailValidator
  • sfUrlValidator
  • sfRegexValidator
  • sfCompareValidator
  • sfFileValidator
  • sfCallbackValidator

Vous trouverez des exemples clairs d’utilisation sur cette page

Sécurité

Ce système de validation est très efficace et bien pensé. Il va nous permettre de gérer les erreurs attendues dans nos applications en les séparant du traitement « normal » toujours pour plus de clarté.

Ce point nous offre un atout de sécurité très propre et essentiel pour une bonne application web.

Publié sous  | Mots clés  | aucun commentaires

Symfony et Validator (suite)

Publié par Aline Bunelle Ven 21 déc 2007 17:01:00 GMT

Résumé en image

article presse webpulser

Validation d’un formulaire

Il est toujours assez lourd de développer un formulaire qui gére les erreurs, réaffiche le formulaire (rempli avec les données qui viennent d’être saisies) avec les erreurs sur les champs erronés.

Les types d’erreurs les plus courants sont :
  • Champs non rempli;
  • Login existant;
  • Les deux mots de passe ne correspondent pas;
  • Le mot de passe est trop court;
  • Adresse mail invalide;
  • ... .

Ce genre de traitement va maintenant être très simple à mettre en place. Pour cela nous écrirons un fichier de configuration yaml portnat le nom de notre action dans le dossier validate.

Syntaxe du fichier de configuration de validation

Nous indiquerons dans ce fichier sur quel type de requête doit fonctionner la validation (POST et/ou GET). Puis, nous indiquerons également si nous activons la repopulation des champs et enfin, la validation de nos champs. Voici un exemple de validation :

methods: [post, get]

fillin:
  enabled: on # Active la repopulation

fields:
  login:
    required: yes
      msg: Le champ login doit être rempli.
    sfPropelUniqueValidator:
      class:        User
      column:       pseudo
      unique_error: Ce login est déjà utilisé, merci d'en choisir un autre.

Ici, le champs login ne doit pas être vide, et ne doit pas déjà exister dans la table user champs pseudo.

Ce système est une surcouche de validation aux fonctions validate, les réactions seront les mêmes en cas de validation ou non.

Développement d’un formulaire

Pour que les messages d’erreurs, et la repopulation (si activée) fonctionnent, il ne nous reste que quelques petites choses à ajouter. Tout d’abord l’utilisation du helper Validation :

<?php use_helper('Validation') ?>

Et nous n’avons plus qu’a géré l’erreur et la repopulation :

<table>
    <tr>
        <td colspan="2" align="center"><?= form_error('login') ?></td>
    </tr>
    <tr>
        <td><?= label_for('login', 'Login :') ?></td>
        <td><?= input_tag('login', $sf_params->get('login')) ?></td>
</table>

La fonction form_error nous permettra de récupérer l’erreur (indiquée dans le fichier de configuration). Rien ne s’affiche s’il n’y a pas d’erreur.

$sf_params->get(‘login’) nous permet de récupérer la valeur saisie (eronnée ou non).

Publié sous  | Mots clés  | 1 commentaire

Symfony et Validator

Publié par Aline Bunelle Ven 21 déc 2007 17:00:00 GMT

Symfony intègre un système de validation très bien pensé qui permet une gestion très facile.

Nous distinguerons la validation des formulaires et le reste, c’est à dire les valeurs venant de l’utilisateur GET & POST, et des données récupérées en base.

La fonction Validate

Dans un controleur, une action est définie via la fonction executeNomAction. Tout comme le mot clé execute, le mot clé validate va nous permettre d’associer une fonction de valiation à notre action. Cette fonction devra retourner true ou false selon le résultat de nos tests.

Remarque :Ne rien retourner dans la fonction Validate revient à retourner False.

Succes & Error

Lorsque la fonction validate retourne true, alors la fonction execute est appelée et continue le fonctionnement normal.

Par contre, si la fonction retourne false , la fonction execute ne sera pas traité et la vue nomActionError sera alors utilisée à défaut de nomActionSuccess dans le cas normal.

La fonction HandleError

Tout comme les mots clés execute et validate, le mot clé handleError peut être lié à notre action. elle sera appelée si elle existe (avant d’utiliser la vue nomActionError) et permettra d’effectuer notre traitement en cas d’erreur.

Si la validation passe, Execute sera la fonction de traitement , sinon il s’agira de handleError.

Validation d’une action

Comme avec execute, les mots clés validate et handleError vont précéder le nom de l’action :

public function executeNomAction(){
    // Traitement si l'action est validée
}
public function validateNomAction(){
    // Validation de l'action. return true || false !
}
public function handleErrorNomAction(){
    // Traitement si l'action n'est pas validée
}

Validation des actions d’un controleur

Il est possible que toutes vos actions doivent vérifier une même validation. Dans ce cas vous pouvez déclarer les fonctions validate et handleError sans suffixe.

Remarque : Ces fonctions (généralisées à un controleur) ne seront appelées que si les fonctions (INDEPENDEMMENT) de validation propre à l’action (validateNomAction et/ou handleErrorNomAction) n’existent pas !

Publié sous  | Mots clés  | aucun commentaires

La classe Criteria

Publié par Aline Bunelle Mer 12 déc 2007 16:43:00 GMT

La classe Criteria va nous permettre de formuler nos requêtes SQL.

Remarque : Un objet de type Criteria est attendu par les fonctions d’accès des classes Peer (doSelect; doDelete, ...).

Exemple:
    $c = new Criteria();
    $list = NomTablePeer::doSelect($c);
$list contiendra un tableau d’objets NomTable contenant TOUS les enregistrements de la table NomTable. Nous venons d’effectuer la requête :
    SELECT * FROM nom_table

Criteria et les conditions

Maintenant voyons comment ajouter des conditions à notre requête.

Pour cela, il suffit de les ajouter à notre objet. La méthode add de la classe Criteria prend trois paramètres, respectivement :
  • le champ affecté;
  • la valeur;
  • l’opérande.

Remarque : par défaut l’opérande est ’=’.

Exemple:
    $c = new Criteria();
    $c->add(NomTablePeer::ID, 5);
    $list = NomTablePeer::doSelect($c);

Nous récupérons l’enregistrement de la table nom_tablel’id est égale à 5. (Nous comprenons mieux la correspondance avec la méthode retrieveByPk vu tout à l’heure).

Ici nous allons recupérer aucun ou un enregistrement. En passant par la méthode doSelect, nous récupérerons soit :
  • un tableau vide,
  • un tableau d’une case.
Dans ce cas de figure utilisons la méthode doSelectOne où nous récupérerons soit:
  • une valeur VIDE,
  • directement notre objet NomTable.

La méthode add seule ne nous permettra pas d’effectuer des OR dans nos conditions de selection. Nous utiliserons pour cela les objets de type Criterion.

    $c = new Criteria();
    $criterion = $c->getNewCriterion (
              NomTable::ID, 6
            )->addOr(
                $c->getNewCriterion (
                      NomTable::ID, 8
                )
            );

    $c->addAnd($criterion);
Rien de bien compliqué, ici nous effectuons la requête :
    SELECT * FROM nom_table WHERE id = 6 OR id = 8

Criteria et les opérantes

Evidemment nous n’allons pas nous limiter à l’opérande ’=’, voici comment utiliser nos opérandes avec Criteria :
WHERE column = value         ->add(column, value);
WHERE column <> value         ->add(column, value, Criteria::NOT_EQUAL);
Other Comparison Operators     
> , <                 Criteria::GREATER_THAN, Criteria::LESS_THAN
>=, <=                 Criteria::GREATER_EQUAL, Criteria::LESS_EQUAL
IS NULL, IS NOT NULL         Criteria::ISNULL, Criteria::ISNOTNULL
LIKE, ILIKE             Criteria::LIKE, Criteria::ILIKE
IN, NOT IN                 Criteria::IN, Criteria::NOT_IN
Other SQL Keywords     
ORDER BY column ASC         ->addAscendingOrderByColumn(column);
ORDER BY column DESC         ->addDescendingOrderByColumn(column);
LIMIT limit             ->setLimit(limit)
OFFSET offset             ->setOffset(offset)
FROM table1, table2 WHERE table1.col1 = table2.col2     ->addJoin(col1, col2)
FROM table1 LEFT JOIN table2 ON table1.col1 = table2.col2     ->addJoin(col1, col2, Criteria::LEFT_JOIN)
FROM table1 RIGHT JOIN table2 ON table1.col1 = table2.col2 ->addJoin(col1, col2, Criteria::RIGHT_JOIN)

Le système Relationnel

Voyons enfin l’atout de notre système relationnel mis en place au début.

En indiquant nos clés étrangères, lors de la génération des objets, l’ORM va ajouter les méthodes permettant d’accèder, à partir d’un objet, à un autre objet lié. Plus clairement :

Au lieu de faire :
    $record = NomTablePeer::retrieveByPk(1);
    $c = new Criteria();
    $c->add(NomTable2Peer::NOM_TABLE_ID, 1);
    $record2 = NomTable2Peer::doSelectOne($c);
Nous allons effectuer:
    $record = NomTablePeer::retrieveByPk(1);
    $record2 = $record->getNomTable2();

Ceci nous fera gagner un temps et une clarté considérable dans notre application !

Publié sous  | Mots clés  | aucun commentaires

Les classes Objet et Peer

Publié par Aline Bunelle Mer 12 déc 2007 15:45:00 GMT

Les classes Objet

Une classe Objet va permettre de représenter les enregistrements de chaque table.

Dans notre exemple, les classes NomTable et NomTable2 seront générées ainsi que leurs methodes d’accès selon leurs attributs :
  • getId(); getCreatedAt(); getTitre(); ...
  • setId($id); setCreatedAt($created_at); setTitre($titre); ...

Pour créer une donnée en base, rien de plus simple, claire et rapide:

    $instance = new NomTable();
    $instance->setTitre('Titre 1');
    $instance->setTexte('Texte du titre *1...');
    $instance->save();

    $instance2 = new NomTable2();
    $instance2->setTexte('Peu importe');
    $instance2->setNomTableId($instance->getId());
    $instance2->save();

    $instance2->delete();

L’appel à la méthode save, exécute la requête INSERT INTO ou UPDATE en fonction de l’id (existant ou non en base). En cas d’insertion, une fois en base, l’id est généré et mis à jour dans notre objet. Nous pouvons directement le récupérer via la fonction d’accès getId pour faire le lien relationnel avec notre instance2.

Rappel : Les champs created_at et updated_at vont être automatiquement remplis.

Nous n’écrirons plus les requêtes SQL (INSERT INTO …; UPDATESET …; DELETE FROM…).

Les classes Peer

Une classe Peer permet de récupérer les données en base. Elle ne contient que des méthodes et attributs STATIC.

Ainsi, suivant notre exemple, les classes NomTablePeer et NomTable2Peer seront générées.

Nous n’écrirons plus les requètes (SELECT * FROM …;).Elles seront écrites via la classe Criteria. Nous décrirons le fonctionnement de ce système dans le prochain article.
    $c = new Criteria();
    // Mises en places des critères
    $tab_objet_NomTable = NomTablePeer::doSelect($c)

    foreach ($tab_objet_NomTable as $nom_table)
        echo $nom_table->getId().' '.$nom_table->getTitre().'<br />';

Les classes Peer nous permettront de toujours récupérer les enregistrements en table sous leur forme objet (cf: classe Objet).

Lorsque nos critères nous permettent d’affirmer qu’un seul enregistrement sera retourné, utilisons la méthode doSelectOne qui nous retourne directement l’objet de l’unique enregistrement.

Egalement, la méthode retrieveByPk nous permettra de récupérer un objet directement en fonction de la clé primaire.

    $objet1 = NomTablePeer::retrieveByPk(1);

    $c = new Criteria();
    $c->add(NomTablePeer::ID, 1);
    $objet2 = NomTablePeer::doSelectOne($c);

objet1 et objet2 vont récupérer le même enregistrement.

Publié sous  | Mots clés  | aucun commentaires

L'object Relational Mapping

Publié par Aline Bunelle Mer 12 déc 2007 14:58:00 GMT

Rappelons que Symfony utilise le système MVC et que par conséquent le modèle (l’accès en base de données) et bien distinct du reste (Controleurs et Vues).

L’ORM permet de créer une correspondance entre les tables de la base de données et les objets qui seront générés.

Pour chaque table correspondra une classe NomTable, permettant d’instancier les données de la table, et une classe NomTablePeer qui permettra d’intéragir avec la base de données.

Il est très important de bien comprendre et de différencier ces deux types de classes Objet et Peer qui seront générées pour chaque table. Nous les détaillerons dans le prochain article.

L’ORM suit une règle syntaxique pour le nom des classes et des méthodes :
  • La première lettre est bien sûr une majuscule;
  • Les ’_’ sont supprimés et la lettre qui suit est mise en majuscule;

Les classes sont générées dans lib/model (sachant que le dossier om contient les classes à ne pas modifier, les noms de ces classes sont précédés du mot clé Base).Elles héritent des classes Base.C’est ici que vous effectuerez vos traitements SQL.

Remarque : Si l’on régénére le modèle via la commande propel-build-model seules les classes Base seront écrasées !

Pour que l’explication soit plus clair nous allons nous baser sur un exemple, reprenons la structure précédente :

propel: 
  nom_table:
    id:
    created_at:
    titre:varchar(255)
    texte: longvarchar

  nom_table_2:
    id:
    created_at:
    updated_at:
    nom_table_id:
    texte: varchar(255)
    description: longvarchar
    chiffre: int

Publié sous  | Mots clés  | aucun commentaires