Mikael RANDY

Aller au contenu | Aller au menu | Aller à la recherche

Symfony

Description, explication, astuces concernant le framework français populaire.

Fil des billets - Fil des commentaires

lundi, mars 1 2010

Auto-complétion des commandes Symfony

J'ai découvert au fil de mes flux RSS un petit script qui permet d'obtenir l'auto-complétion du mode CLI de Symfony. Je tente, de cet article, de vous expliquer en quoi il est pratique, et comment l'installer sur votre serveur.

Lire la suite...

dimanche, février 21 2010

Symfony 2 reloaded

Ce mercredi 17 février 2010, en clôture du Symfony Live 2010, Fabien Potencier a présenté Symfony 2 qui porte le nom de code de "Symfony reloaded". En voici un retour en quelques points clés.

Lire la suite...

vendredi, janvier 29 2010

Créer une classe parent pour tout le modèle avec Doctrine

Pour qui a déjà utilisé Doctrine de manière un tant soit peu avancé, une problématique a dû se poser à un moment où à un autre : Comment faire pour définir un comportement pour tout mon modèle ?
Dernièrement, j'ai voulu mettre en place un système loguant toutes les suppressions, pour des besoins de tracabilité. La solution la plus simple me semblait être de pouvoir surcharger la méthode de suppression générique au niveau de mon projet.

Or, lorsque l'on regarde d'un peu plus près l'arborescence de classe généré par Doctrine, on peut se rendre compte qu'il manque d'une classe générique à tout le modèle, au niveau du projet et qui permettrait de faire ce que je cherchais à faire.

Le fonctionnement de base de Doctrine

Doctrine gère 2 types de classes, qui ont chacune une arborescence bien spécifique : les classes de tables et les classes d'entités.
Les classes de tables permettent de gérer les collections, c'est à dire les groupes d'enregistrements
Les classes d'entités permettent la gestion des unités, c'est à dire chaque enregistrement indépendamment.

Si votre projet nécessite la présence, par exemple, de 2 tables : "User" et "Right", Doctrine va générer l'arborescence de classe suivante : doctrine-diagrame_model_1.png

Comme on peut le remarquer, toutes les classes qui se trouvent dans le projet sont gérées indépendamment, et la 1ère classe générique se trouve au niveau du coeur de symfony. Dans le sfDoctrinePlugin, pour être précis.

Ajouter un niveau de classe avec Doctrine

Afin de pouvoir disposer d'un niveau de classe supplémentaire, au niveau du projet, il est possible de configurer Doctrine pour que les classes "Base" et "BaseTable" héritent d'autres tables que celles par défaut (respectivement sfDoctrineRecord et Doctrine_Table).
Pour agir sur la configuration de Doctrine, il vous faut éditer le fichier "config/ProjectConfiguration.class.php" comme suit :

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    // ...
 
    // custom builder options for doctrine
    sfConfig::set('doctrine_model_builder_options', array(
      'baseTableClassName' => 'MyDoctrineTable',
      'baseClassName' => 'MyDoctrineRecord'));
  }
}

Ainsi, avec ces configurations, on demande à Doctrine de faire en sorte que toutes les classes "Base" héritent de la classe "MyDoctrineRecord" et que les classes "BaseTable" héritent de la classe "MyDoctrineTable", comme illustré dans l'exemple suivant : doctrine-diagrame_model_2.png

Ces classes là doivent bien entendu elles même se remettre dans l'arborescence de classe.
Les squelettes de base de ces classes doivent donc être les suivants :

class MyDoctrineTable extends Doctrine_Table
{
    // ...
}
 
class MyDoctrineRecord extends sfDoctrineRecord
{
    // ...
}

A partir de là, il suffit de régénérer le modèle pour que la nouvelle arborescence soit disponible :

symfony doctrine:build-model

Et, de cette manière, il est donc possible d'agir sur toutes les opérations réalisées par le modèle depuis ces classes.

mardi, décembre 22 2009

Enregistrer une collection d'objet, avec contrôle de fonctionnement

Ce jour, un de mes développeurs a fait une découverte sympathique sur Doctrine.

Le besoin de base était le suivant : On manipule une liste d'objet (création, affectation de contenu) dans l'optique de les sauvegarder.

La syntaxe que nous tout un chacun est tenté de faire est la suivante :

try
{
  for($i = 0; $i < 10; $i++)
  {
    $item = new Item();
    $item->setValue($i);
 
    $item->save();
  }
} catch (sfException $e) {
  echo "An error occurs while data saving"
}

Or, le soucis, avec cette méthode, c'est que si une erreur intervient au cours de la sauvegarde, par exemple, pour l'item n°5, eh bien en base, nous aurons les items correctement enregistrée (entre 0 et 4), mais il manquera la fin (de 5 à 9).

La solution est simple, me direz vous, il suffit d'ouvrir une transaction avant, et de la fermer après. Eh bien oui, vous avez raison, mais j'ai trouvé encore plus élégant : les collections Doctrine (Doctrine_Collection). Si vous êtes un habitué de Doctrine, vous avez déjà croisé cette classe puisque Doctrine remplace les tableaux par des instances de celle-ci.

Toujours est-il que l'équivalent du code précédent, mais en prenant en compte la transaction, et en utilisant les Doctrine_Collection est le code suivant :

$items = new Doctrine_Collection('Item', 'id');
try
{
  for($i = 0; $i < 10; $i++)
  {
    $item = new Item();
    $item->setValue('value ok');
    if($i == 5)
      $item->setValue('error value');
 
    // Add item into Doctrine_Collection
    $items->add($item);
  }
 
  // Save collection content, in a SQL transaction
  $item->save();
 
} catch (sfException $e) {
  echo "An error occurs while data saving"
}

Je trouve ça tellement plus pratique et élégant que je suis tombé sous le charme de cette méthode.