Un hack des sfForm sur des modèles Sluggable

De e-glop
Révision datée du 22 mars 2013 à 21:56 par BeTa (discussion | contributions)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)

Constat

Les sfForm bâtis sur des modèles implémentant le behaviour Sluggable plantent si deux objets ont les mêmes données permettant de calculer le slug : en effet, avant comparaison dans la base, les calculs des deux slugs donnent le même résultat.

Pour éviter les slugs dupliqués, Doctrine 1.2 appelle juste avant l'insert/update la fonction Doctrine_Template_Listener_Sluggable::getUniqueSlug() qui va chercher les doublons potentiels dans la base de données et qui offre une alternative unique pour le Doctrine_Record courant.

Le problème est que Symfony pour sa part, vérifie dans ses sfForm l'unicité du slug pré-calculé mais non (encore) confronté au contenu de la table courante. Cela se passe dans le BaseForm courant, généré automatiquement au moment du ./symfony doctrine:build-forms via le template lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineForm/default/template/sfDoctrineFormGeneratedTemplate.php.

Solution

Soit nous optons pour un hack pas très joli, soit on reprend le template. C'est cette seconde option, plus propre (même si elle touche au core de Symfony1), que nous choisissons.

// lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineForm/default/template/sfDoctrineFormGeneratedTemplate.php
// Ligne 36:
<?php if ($uniqueColumns = $this->getUniqueColumnNames()): ?>
    $this->validatorSchema->setPostValidator(
<?php if (count($uniqueColumns) > 1): ?>
      new sfValidatorAnd(array(
<?php foreach ($uniqueColumns as $uniqueColumn): ?>
        new sfValidatorDoctrineUnique(array('model' => '<?php echo $this->table->getOption('name') ?>', 'column$
<?php endforeach; ?>
      ))
<?php else: ?>
      new sfValidatorDoctrineUnique(array('model' => '<?php echo $this->table->getOption('name') ?>', 'column' $
<?php endif; ?>

Modifications à apporter

Index: lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineForm/default/template/sfDoctrineFormGeneratedTemplate.php
===================================================================
--- lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineForm/default/template /sfDoctrineFormGeneratedTemplate.php	(révision 33643)
+++ lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineForm/default/template/sfDoctrineFormGeneratedTemplate.php	(copie de travail)
@@ -32,7 +32,10 @@
 <?php endforeach; ?>
     ));
 
+<?php // LIBRINFO: 22/03/2013
 <?php if ($uniqueColumns = $this->getUniqueColumnNames()): ?>
+<?php foreach ( $uniqueColumns as $key => $ucol ) if ( implode("', '", $ucol) === 'slug' ) unset($uniqueColumns[$key]); ?>
+<?php if ( count($uniqueColumns) ): ?>
     $this->validatorSchema->setPostValidator(
 <?php if (count($uniqueColumns) > 1): ?>
       new sfValidatorAnd(array(
@@ -44,6 +47,7 @@
       new sfValidatorDoctrineUnique(array('model' => '<?php echo $this->table->getOption('name') ?>', 'column' => array('<?php echo implode("', '", $uniqueColumns[0]) ?>')))
 <?php endif; ?>
     );
+<?php endif ?>
 
 <?php endif; ?>
     $this->widgetSchema->setNameFormat('<?php echo $this->underscore($this->modelName) ?>[%s]');

Et là nous reconstruisons les formulaires :

./symfony doctrine:build-forms

Limites

Cette solution est basée sur le nom "slug" de la colonne visée. ce n'est donc pas l'idéal. Disons que pour le moment "ça suffira", en attendant de faire ça mieux.