Blog d'un développeur multi-support

[DIM] pour les intimes :)

Symfony 1.2, behavior doctrine en actions

J’ai (re)découvert un truc vraiment sympathique au boulot c’est le système de behavior doctrine intégré à .

Hein ? Mais à quoi ça sert ?

Ce que j’en retiens c’est que cela peut permettre d’automatiser certaines actions (répétitives) à l’enregistrement en base, et donc d’enrichir son modèle et deux coup de cuillère à pot.

Prenons un exemple, vous devez faire un site basé sur la créations de contenus par vos utilisateurs. Ils peuvent écrire des billets, commenter, s’envoyer des messages privés, gérer un annuaire .. bref un site où l’on doit quasiment tout relié à un utilisateur. Chaques tables auraient donc au moins un « user_id » comme clée étrangère ce qui implique dans vos actions d’associer l’utilisateur courant à l’objet pour le sauvegarder .. et ce pour chaque table … lourd non ?

Hé bien grâce aux behaviors vous pouvez sortir ce comportement générique dans des classes et avec seulement de la configuration au niveau de votre schéma, vous pouvez associer ce comportement à n’importe quel table. L’exemple que j’ai pris est tiré du behavior « Signable » du plugin sfDoctrineActAsSignablePlugin.

Configuration

1
2
3
4
5
6
7
8
9
Item:
  actAs:
    Signable:
  columns:
    id:
      type:             integer
      primary:          true
      autoincrement:    true
    champ1:             string(255

Comment ça marche ?

Donc si on résume, il faut dire à Doctrineque l’on va rajouter des colonnes .. et que l’on veut être prévénu lors de l’insertion en base pour associé nos actions. Donc avec seulement 2 classes, une de définition et une d’écoute, on peut s’en sortir et crée notre behavior.

Lors de la configuration, en rajoutant « actAs : Signable », Doctrine va chercher une classe de définition qui ira  étendre une classe Doctrine_Template. Par convention il ira chercher une classe nommé Doctrine_Template_Signable. Le rôle de cette classe est de déclarer ses colonnes et de rajouter une écoute, un listener, vers l’autre classe d’actions.

Pour mon exemple, je vais volontairement raccourcir les classes du sfDoctrineActAsSignablePlugin et prendre pour acquis que vos utilisateurs sont gérés via sfGuardDoctrinePlugin.

Doctrine_Template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Doctrine_Template_Signable extends Doctrine_Template {
 
  // Définitions des relations
  public function setUp() {
  // Bim rajoute une clée étrangère sur sfGuard
  $this->hasOne('sfGuardUser as Author', array(
            'local' => 'created_by',
            'foreign' => 'id'
            )
            );
  }
 
 public function setTableDefinition() {
 
  // Bim une colonne en plus
  $this->hasColumn('created_by', 'integer', 4, array(
          'type' => 'integer',
          'length' => '4',
     ));
 
  // Lien avec notre 2eme classe .. on passe le nom de la colonnette rajouté
  $this->addListener(new Doctrine_Template_Listener_Signable('created_by));
  }
}

Là pour l’exemple aucune configuration au niveau du schéma n’est possible mais c’est facile à rajouter via d’un tableau d’options et le constructeur suivant :

1
2
3
  public function __construct(array $options = array()) {
    $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
  }

Doctrine_Template_Listener

Nous avons vu que notre Template rajoute un listener vers notre 2e classe en lui passant la colonne à surveiller (dans la vrai vie, un tableau d’options ..). Le rôle de notre listener de pouvoir agir avant/après l’insertion/édition d’un objet .. facile :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Doctrine_Template_Listener_Signable extends Doctrine_Record_Listener {
 
  protected $_colonette = "created_by";
 
  public function __construct($colonne) {
    $this->_colonette = $colonne;
  }
 
  public function preInsert(Doctrine_Event $event) {
      $createdName = $this->_colonette;
 
      // recupère l'objet appellant :
      $objet = $event->getInvoker();
 
      // Affectation de valeur
       $objet->$createdName = $this->getUserId();
  }
 
  public function getUserId() {
     // Echappe le mode cli
     if (0 != strncasecmp(PHP_SAPI, 'cli', 3)) {
       // L'user courant
       return sfContext::getInstance()->getUser()->getAttribute('user_id', null, 'sfGuardSecurityUser');
     } else {
      return null;
     }
  }
 
  public function preUpdate(Doctrine_Event $event) {
  }
 
  public function postUpdate(Doctrine_Event $event) {
  }
 
  public function postInsert(Doctrine_Event $event) {
  }
 
  // A tester : public function Delete(Doctrine_Event $event) ..
}

Vous n’avez plus qu’a rebuilder votre model et magie ça doit marcher !!

Vu que l’on a fait une relation de type One-to-many sur sfGuard vous pouvez accéder à l’utilisateur sfGuard via la propriété Author sur n’importr quel table marqué comme « Signable »

1
  echo $objet->Author->id;

Voilà .. simple au final non ? Ah vous de créez le votre, :p

Comme on dirait au boulot « Amaaziiiing !! »

Tags : , , , ,

phpFlickr & Flex

Pour un cours de Flex, il nous avait été demandé de créer une application très simple constitué de deux écrans pour nous apprendre à manipuler les « states », les composants personnalisés, le « data binding » etc.

Le sujet de l’application était d’afficher des images en fonction d’une ville – en vue de faire du data binding sur un XML « statique » de villes – et de récupérer des images issues d’un XML dynamique via l’objet HTTPService.

Alors quitte à faire du dynamique, autant interroger directement un web service existant en vue de récupérer pleins d’images sans se prendre la tête. Et là j’ai pensé à FlickR et sa monstrueuse API. Après 5mn montre en main j’ai eu ma clé pour accèder à l’API et 4mn après je découvrait phpFlickR.

Upload de la librairie et voici mon bout de script qui me renvoit suivant deux paramètres en $_GET un joli XML près à être utilisé en .

  • $_GET['town'] est le terme rechercher dans l’api FlickR sur les tags et le text qui commente une photo.
  • $_GET['max'] est le nombre de photos qu’il faudra retourner.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
require_once("phpFlickr.php");
$flickR = new phpFlickr("ta_cle_de_connection_flickR");
 
// 10 résultat par défaut ..
if(isset($_GET['max']))
$max = $_GET['max'];
else $max=10;
 
$search = array("tags"=>$_GET['town'],'per_page'=>$max, "tag_mode"=>"any",'text' => $_GET['town']);
$tab = $flickR->photos_search($search);
 
// On renvoit un XML à Flex
header("content-type: application/xml");
 
// La balise qui va bien
echo "<?xml version="1.0" encoding="utf-8" ?>";
 
for ($i = 0; $i != $max; $i++)
{
        // on s'assure d'avoir des photos valides
	if($tab['photo'][$i]['id'] != "")
	{
	// Simple de créer des urls FlickR non ?
	$url = $flickR->buildPhotoURL($tab['photo'][$i],"thumbnail");
	$link = $flickR->buildPhotoURL($tab['photo'][$i],"medium");
 
	echo "<image><url>".$url."</url><urlLarge>".$link."</urlLarge></image>";
	}
}

La partie Flex est tout aussi simple, voici la partie essentielle qui nous intéresse :

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="searchPhoto('france',20)">
 
<mx:Script>
<![CDATA[
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
 
[Bindable]
private var servicePhoto:ArrayCollection = new ArrayCollection(); 
 
 
private function searchPhoto(search:String,max:int):void
{
	var modService:HTTPService = new HTTPService();
	modService.method = "get";
	modService.url = "http://oni-ecchi.info/labo/FlexPhotos/service.php";
 
	var parameters:Object = new Object();
	parameters.town = search;
	parameters.max = max;
	modService.request = parameters;                
	modService.send();
	modService.addEventListener(ResultEvent.RESULT,onDataCharged);                
}
 
private function onDataCharged(e:ResultEvent):void
{
	servicePhoto = new ArrayCollection();
	servicePhoto = e.result.root.image as ArrayCollection;		
	vignettes.dataProvider = e.result.root.image as ArrayCollection;
}
 
]]>
</mx:Script>
<mx:TileList id="vignettes" dataProvider="{servicePhoto}" width="481" height="279" columnWidth="110" rowHeight="110" columnCount="4" horizontalCenter="0" paddingRight="5" verticalCenter="0">
    <mx:itemRenderer>
   		<mx:Component>
   			<mx:Image  source="{data.url}" verticalAlign="middle" horizontalAlign="center" />
   		</mx:Component>
    </mx:itemRenderer>       
</mx:TileList>
 
 
</mx:Application>

Ce n’est qu’un exemple très simple. Dans le fichier source qui va suivre, j’ai un peu plus travaillé la chose en utilisant un composant de chargement d’image sympathique, un spinner de chargement 2.0 et enfin la gestion des states (très simple).

Voilà, c’est tout pour aujourd’hui

Tags : , , ,

Et un framework un !

Aller sur http://blog.jaysalvat.com/articles/model-baker-une-interface-graphique-a-la-creation-dapplications-cakephp.php ou regarder simplement son screencast :

Model Baker in action from Jay Salvat on Vimeo.

Ca a l’air génial :) Dommage que ça soit seulement pour mac pour l’instant, mais c’est très prometteurs ! Apres un tour sur , sur Zend, faudrait que j’aille voir CakePHP donc :)

Tags : , , , ,

Utiliser une procédure stocké mysql avec Zend !

Dans un article précédent, je créais la « import », voilà comment je l’utilise dans Zend.

Je n’invente rien, j’ai trouvé la manip sur http://www.ericmery.fr/blog/zend_db-sql-server-procedure-stockee.

1
2
3
4
5
6
7
8
9
10
$registry = Zend_Registry::getInstance();
$config = $registry->get("config");
 
$db = Zend_Db::factory($config->db);
 
$stmt = $db->prepare("call import(?,?,?);");
$stmt->bindParam(1, $param1, PDO::PARAM_STR,1000);
$stmt->;bindParam(2, $param2, PDO::PARAM_STR, 255);
$stmt->bindParam(3, $param3, PDO::PARAM_STR, 255);
$res = $stmt->execute();

Magie, c’est la même chose. On remercie PDO et son abstracteur de base de donnée utilisé !

Tags : , , , ,

[Mysql] Procédure Stocké

Pour un projet d’annuaire de Flux RSS, j’ai du faire en sorte que l’on puisse importer ses Flux dans l’annuaire via un fichier XML opml que tout bon agrégateur (google reader, netvibes) fournit. Donc admettons que l’on a 100 flux RSS à importer il faut pour chaque flux vérifier si le flux n’existe pas déjà sinon le créer en base. Donc il faut prévoir un maximum de 200 requêtes SQL.

Seulement voilà, si on fait les instructions de test d’existence et d’insertion au niveau , cela fait 200 aller retour entre et la base . Hors ce qui plombe un script c’est souvent ces aller retour incessants .. Un moyen d’optimiser ce traitement a été de créer une qui prend en paramètre le fichier XML (où plutôt une représentation épuré plus strict), et de faire exécuter les 200 requêtes DANS Mysql. Chouette non ? Plus rapide, plus stable, plus optimisé .. que du bonheur.

Procédure Stocké en Mysql

Vous allez rire, mais d’après mes recherches googlidiennes très peu de personnes font des procédures en Mysql. Cela vient sans doute du fait qu’elles sont apparru avec mysql 5 et que c’est encore « neuf ».

Tous les exemples que l’on trouve sur le sujet sont pour SQL serveur PRo, donc du transact-SQL. Et le souci c’est qu’il n’y pas forcement d’équivalence entre les fonctions transact-SQL et les fonctions mysql. La synthaxe n’est décidement pas la même.

Outil utilisé

Mysql query browser !

L’exemple que je donne n’est valable qu’a travers sont interface .. zarb.. m’enfin c’est un très bon outil quand même, rien à voir avec un phpMyAdmin tout POURRI !

Création de la requête

Voilà la précédure allégé que j’ai crée (je fais d’autres test en version un peu plus lourde). Je vais essayer de la commenter au mieux.

La procédure s’appelle import, elle est dans la base de donné nommé « Fluxe » et on l’appelle et l’utilise comme ceci

1
2
3
4
call import(
"premier flux|http://monsite|http://monsite/fluxrss|description;
deuxieme flix|http://monsite2|http://www.google.fr/bidule.xml|autre description;
troisieme|http://monsite3|http://monsite2/rss.xml|description;",3);

Le premier paramètre est la chaine retravaillé coté PHP qui correspond à chaque ligne de l’XML, et le deuxième paramètre est le nombre de flux. Une entré se fini via un « ; » et chaque colonne est séparé via un  » | « . Bon je sais, mes délimiteurs ne sont pas assez complexe dans cet exemple et cela serait risque de bug si une description ou url comporterait des points virgules et des pipes…

Voici la requête :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
DELIMITER $$  -- pas trop compris l'interet O_o
DROP PROCEDURE IF EXISTS `fluxe`.`import` $$
-- Supprime la procédure si elle existe (je suis fort en anglais hein ? ^^) 
 
CREATE DEFINER=`root`@`localhost` PROCEDURE `import`(In myCsv Text, In nbElement Int)
-- crée la procédure avec deux paramètres "IN" de type Text et Int. C'est à dire que c'est deux paramètres obligatoires pour exécuter la requête.  
 
BEGIN -- Debut de la procédure
 
-- Declare mes variables correspondant aux colonnes de ma table Flux
DECLARE nom varchar(255);
DECLARE urlSite varchar(255);
DECLARE myurlFlux varchar(255);
DECLARE description text;
DECLARE  ligne_flux VARCHAR(1000); -- correspondra à une ligne de Flux
DECLARE cpt int; -- un petit compteur pour faire une jolie boucle While
 
set cpt = nbElement;  -- / !  Voilà comment on définit une variable SET var = valeur; 
 
WHILE cpt <= nbElement and cpt > 0 do  -- j'aurais pu utiliser une boucle Loop aussi.
 
  select replace(substring(substring_index(myCsv, ';', cpt), length(substring_index(myCsv, ';', cpt - 1)) + 1), ';', '') as ligne into ligne_flux;
 
  -- Outch "KEKECER ?" vous devez vous pausez comme question :)
  -- c'est simple, je découpe le premier paramètre In pour en extraire la 1er ligne trouvé.
  -- Les fonctions appellés ne sont pas du php mais bien du SQL ! Puissant non ?
  -- l'idée de récupèrer la 1er ligne DANS une variable "ligne_flux", d'où le "select ... INTO variable" 
 
  -- Parsage de la chaine "ligne_flux" pour en extraire les colonnes, rebelote quoi ..
  select  replace(substring(substring_index(ligne_flux, '|', 1), length(substring_index(ligne_flux, '|', 1 - 1)) + 1), '|', '') into nom;
  select  replace(substring(substring_index(ligne_flux, '|', 2), length(substring_index(ligne_flux, '|', 2 - 1)) + 1), '|', '') into urlSite;
  select  replace(substring(substring_index(ligne_flux, '|', 3), length(substring_index(ligne_flux, '|', 3 - 1)) + 1), '|', '') into myurlFlux;
  select  replace(substring(substring_index(ligne_flux, '|', 4), length(substring_index(ligne_flux, '|', 4 - 1)) + 1), '|', '') into description;
 
  -- Insertion que si la ligne n'existe pas. Le critère est l'url du flux.
  if(NOT EXISTS (select * from flux where urlFlux = myurlFlux)) THEN
       insert into flux (nom,urlSite,urlFlux,description) values (nom,urlSite,myurlFlux,description);
   end if;
 
 -- on décrémente le compteur
 set cpt = cpt - 1;
end while;
 
END $$
DELIMITER ;

Et voilà c’est fini !

Lien de référence

La base :http://dev.mysql.com/doc/refman/5.0/fr/stored-procedure-syntax.html

les variables : http://dev.mysql.com/doc/refman/5.0/fr/variables-in-stored-procedures.html
If, while, case, et tous les contrôles : http://dev.mysql.com/doc/refman/5.0/fr/flow-control-constructs.html

Mis à part la documentation officiel, j’ai rien trouvé d’autres à me mettre sous la dent ..

Tags : , , , ,

Hébergement de BOT

Dans un article précédent, nous avions vu comment créer un bot en . Seulement assurer la connexion du bot sur le salon était assez laborieux. Un processus par bot qui tourne sur votre machine, c’est pas folichon.

Mais grâce à http://new.imified.com, ce souci n’en est plus un ! Ce service web permet de connecter un bot à MSN, IRC, Gtalk etc et ce bot demandera à votre page PHP ce qu’il doit faire.

Au final nous n’avons plus qu’a se consacrer sur les réponses et les traitements à faire en fonction du dialogue de l’utilisateur (donnée récupérer sous forme de $_GET). Fini le parsage du stream pour détecter la bonne ligne ^^

Je vous avouerais, j’ai pas encore tester. Mais je compte le faire un de ces quatres :) Je pense que c’est LA solution pour créer rapidement un compagnon virtuel … Imaginer un bot de ce style qui pilote votre twitter, facebook etc. Vous lui parlez, il s’occupe du reste :)

Vu sur le channel #-fr (irc.freenode.com) grâce à [MA]Pascal.
Il a crée un bot qui relaie ce que vous lui dites sur un site donné. (Système de prise de notes rapide)
Ajouter paste@bot.im à votre compte Gtalk et parler lui. Enfin, cela reste surtout un bot de test, réalisé avec Symfony 1.2 et Doctrine (un ORM comme propel)

Tags : , ,

Validé un formulaire en Ajax avec Symfony 1.1

Dans ce billet nous allons voir comment utiliser Ajax pour valider champ par champ un formulaire constuit avec sfForm de 1.1.

Je prend pour acquis que vous ayez lu / que vous connaissez la validatation de formulaire « classique » offert par sfForm. Tout est expliqué et surtout .

En fait le souci avec les validateurs symfony 1.1 est qu’ils ne possèdent pas la méthode isValid() contraitement aux validateurs symfony 1.0. Du coup, quand on fait de l’ajax, nous sommes obligés de passer d’un type de validateur à un autre.

Cette exemple illuste le fait avec le validateur sfValidatorEmail (sf 1.1) et sfEmailValidator (sf 1.0). L’idée est de définir les paramêtres du validateur (les messages d’erreurs dans notre cas) qu’à un seul endroid afin de respecter le concept Dry (Don’t Repeat Yourself).

Voilà les étapes nécessaires pour mettre en place une validation de formulaire classique et « Ajaxé » :

  1. Vous créez vos widgets dans lib/form/MaClasseForm.class.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    class MaClasseForm extends BaseMaClassForm
    {
    	public function configure()
    	{
    		$this->setWidgets(array(
    		'id'         => new sfWidgetFormInputHidden(),
    		'FirstName' => new sfWidgetFormInput(),
    		'LastName'  => new sfWidgetFormInput(),
    		'Email'      => new sfWidgetFormInput(),
    		'password_repeat'      => new sfWidgetFormInput(),
    		'password'      => new sfWidgetFormInput()
    		));
     
                    // On peut modifier les labels si besoin est ...
    		$this->widgetSchema->setLabels(array(
    		'FirstName'    => 'Prenom',
    		'Email'   => 'addresse Email ',
    		'LastName' => 'Nom',
    		));
            }
    }
  2. Vous définissez vos validateurs dans lib/form/MaClasseForm.class.php à la suite de la méthode configure()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
     
     $this->setValidators(array(
    'id'         => new sfValidatorPropelChoice(array('model' => 'Utilisateurs', 'column' => 'id', 'required' => false)),
    'FirstName' => new sfValidatorString(array('max_length' => 20, 'required' => false)),
    'LastName'  => new sfValidatorString(array('max_length' => 20, 'required' => false)),
    'password'      => new sfValidatorString(array('max_length' => 40, 'required' => false)),
    'password_repeat' => new sfValidatorString(array('max_length' => 40, 'required' => false)),
    'Email'      => new sfValidatorEmail(array('required' => true)),
    'Birthday'   => new sfValidatorDate(array('required' => false)),
    ));
     
    // J'en profite pour definir un nom formater des champs pour avoir $_POST['utilisateurs['email']] etc
    $this->widgetSchema->setNameFormat('utilisateurs[%s]');
  3. Normallement à ce stade vous pouvez utiliser la classe de formulaire dans votre fichier apps/MonApp/MonModule/actions/actions.class.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    public function executeMonAction() {
     
       $this->formInscription = new MaClasseForm();
       if ($this->request->isMethod('post'))
       {
          // récupère les champs du formulaire sous forme de tableau.
         $array_champ = $this->getRequest()->getParameter("utilisateurs");
         $this->formInscription->bind($array_champ);
         if ($this->formInscription->isValid())
    	{
           // Formulaire valide.
           }
    }
  4. Voilà la validation « classique » et faite. Et l’ajax dans tout ça ? On y vient ^^ Alors au lieu d’afficher le formulaire de manière classique (via le template adéquate apps/MonApp/MonModule/templates/MonActionSuccess.php)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    // Au lieu de
    echo $form;
     
    // On va rendre le template comme on le souhaite
    // Par souci de flemme je ne fait que le champ Email
    <table>
      <tr>
        <td><?php echo $form['Email ']->renderLabel() ?>:</td>
        <td><?php echo $form['Email '] ?></td>
       <td><span id="error_for_email ">
                    <?php if ($form['Email ']->hasError()): ?>
    		<ul class="error_list">
    		<?php foreach ($form['Email ']-<getError() as $error): ?>
    			<li class="error_for"><?php echo $error ?></li>
    			<?php endforeach; ?>
    		</ul>
    		<?php endif; ?> </span></td>
      </tr>
    </table>
  5. Et maintenant on va jouer avec l’ajax pour vérifier ce champ email lors de la saisie.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    /************** Ajax Time :) *****************/
    // Dans le template on met :
    <?php
    // Crée l'observateur pour le champ utilisateurs_email du formulaire.
    echo observe_field("utilisateurs_email",array(
    'update' => "error_for_email",
    'url' => "MonModule/VerifierEmail",
    'with' => "'email='+ value",
    ));
    ?>
  6. On rajoute l’action VerifierEmail() dans le fichier d’actions.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
     
    public function executeVerifierEmail()
    	{
    	 // Initialisation des variables internes
    	 $chaine_a_verifier = $this->getRequestParameter("email");
    	 $array_erreur = array();
     
    	 // Récupère le validateur d'email définit dans lib/form/MaClasseForm.class.php
    	 $form = new MaClasseForm();
    	 $validateur_form = $form->getValidatorSchema();
    	 $all_validateurs_field = $validateur_form->getFields();
    	 $email = $all_validateurs_field["email"];
     
    	 // Récupere le bon code d'erreur
    	 if($chaine_a_verifier == "")
    	 {
    	 	$array_erreur =	array('email_error' => $email->getMessage("required"));
    	 }
    	 else
    	 {
    	 	$array_erreur =	array('email_error' => $email->getMessage("invalid"));
    	 }
     
    	 // Crée un validateur d'email hérité de Symfony 1.0
    	 $validateur_email = new sfEmailValidator($this->getContext());
     
             // Un peu de Dry (Don't Repeat Yourself) afin d'éviter d'avoir des messages d'erreurs définit à deux endroits différents.
    	 $validateur_email->initialize($this->getContext(), $array_erreur  );
    	 // Dispatcheur vers le bon templates
    	 if($validateur_email->execute($chaine_a_verifier,$message_retour))
    	 {
    	 	$message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>true));
    	 }
    	 else
    	 {
    	 	$message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>false));
    	 }
     
    	 return $this->renderText($message_a_afficher);
    	}
  7. Donc en fait on met à jour le « span » error_for_email avec le résultat de l’appel de la méthode VerifierEmail(). J’ai utilisé un template partial car le but est de le réutiliser pour tous les appels en Ajax. Celui ci n’est pas bien compliqué
    1
    2
    3
    4
    
    <ul>
        <li class="<?php if($reussi) echo "accept_for"; else echo "error_for"; ?>">&amp;nbsp;<?php echo $message; ?>
        </li>
    </ul>
  8. Et voilà, le formulaire fonctionne classiquement et dispose de vérifications en Ajax aussi.

Nous venons de voir le validateur d’Email, mais tous fonctionnent sur le même principe. Voici la méthode pour utiliser le validateur de Sting qui diffère un peu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
   public function executeVerifierPseudo()
   {
     // Initialisation des variables internes
    $chaine_a_verifier = trim($this->getRequestParameter("pseudo"));
 
    // Récupère le validateur de pseudo
    $form = new MaClasseForm();
    $validateur_form = $form->getValidatorSchema();
    $all_validateurs_field = $validateur_form->getFields();
    $validateurs = $all_validateurs_field["FirstName"];
    $pseudo = $validateurs->getValidators();
    $pseudo = $pseudo[0];
 
    // Crée un validateur de String sf1.0
    $validateur_pseudo = new sfStringValidator($this->getContext());
    $validateur_pseudo->initialize($this->getContext(), array(
    'min' => $pseudo->getOption("min_length"),
    'min_error' => $pseudo->getMessage("min_length"),
    'max' =>$pseudo->getOption("max_length"),
    'max_error' => $pseudo->getMessage("max_length"),
    'required' => $pseudo->getMessage("required"),
    ));
    // Dispatcheur vers le bon templates
    if($validateur_pseudo->execute($chaine_a_verifier,$message_retour) &amp;&amp; $chaine_a_verifier != "")
    {
       $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>$bool));
    }
    else
    {
     if($chaine_a_verifier == "") $test = $pseudo->getMessage("required");
 
     // Remplace les jokers des messages d'erreurs.
     $min = $pseudo->getOption("min_length");
     $max = $pseudo->getOption("max_length");
     $message_retour=    preg_replace("#%value%#",$chaine_a_verifier,$message_retour);
     $message_retour=    preg_replace("#%min_length%#",$min,$message_retour);
     $message_retour=    preg_replace("#%max_length%#",$max,$message_retour);
     $message_a_afficher = $this->getPartial("ajax_error",array("message"=>$message_retour,"reussi"=>false));
   }
 
   return $this->renderText($message_a_afficher);
}

Et voilà c’est fini :)

Tags : , , , ,

Découverte : BOT IRC

Bonjour, j’ai pu découvrir comment créer un bot en grâce à la communauté IRCienne de . (Qu’est ce qu’IRC ? Qu’est ce qu’un Bot ?). Tout est expliqué sur cette page.

Squattant en ce moment le chan (salon de discutions) de symfony-fr, je passe mon temps à remercier les uns les autres. Depuis peu même j’arrive à aider les nouveaux ^^. Je me suis donc dit que ca serait bien d’avoir un système pour compatibiliser le nombre de « merci » par personnes et d’en sortir quelques statistiques.

Ainsi est née le bot qui sert à avoir une système à points. Chacun peut donc donner, ou enlever un point, et le robot fera le compte.

Fonctionnement

Après quelques discutions sur le channel de symfony, j’ai mis au point 4 petites commandes que le bot va interpréter :

!point guillaume
!malus guillaume
!bonus guillaume
!total

Comment ca marche ?

Le bot est écrit en PHP 5 et se lance via une ligne de commande du style :

php -f utilisation.php

Il n’y a que peu de classes pour le faire fonctionner :

  1. Une classe de connexion qui s’occupe de créer un flux de donnée (une socket via fsockopen), de se connecter à un channel irc,  de pouvoir transmettre des commandes IRC et de récupérer ligne par ligne ce qui se passe sur le channel.
  2. Une classe principal qui permet de garder le flux de donné ouvert (et donc fait en sorte que le bot reste sur le channel IRC), et qui permet de lancer des Plugins. Son rôle tient plus d’un controlleur PHP que d’une classe à mon sens.
  3. Une interface et une classe de base pour la gestion des Plugins. En fait un plugin est une classe qui contient le coeur d’un programme et permet de réagir vis à vis d’un évènement sur le channel. Un plugin fait donc office d’observateur puisqu’il permet d’écouter des évènements mais il s’obverse lui même vu qu’il traite ces évènements et y répond.

J’ai donc créer un plugin et modifier un temps soit peu la classe principal pour avoir à tout moment la liste des utilisateurs du channel. Au final le n’est pas bien compliquer et bien long (330 lignes à tout casser). Les points sont stocker dans une base de données local (voui, pas besoin de serveur en ligne vu que l’on lance le bot en local on peut accèder à une base en local ^^). Quelques random rendent le bot sympathiques pour avoir des phrases de réponses un peu plus humaine.

Je ne vous donne pas le code source de cette petite application car je le juge « pas très propre » et je pense y faire encore pas mal de modifications dessus :)

Pour la suite

Je prévois de faire une application symfony pour ajouter/modifier des phrases de réponses clées. Faire des graphiques de statistiques, et ré-initalisé la base de données à souhait. Tout n’est pas encore fixé, mais j’aimerais bien une console d’administration du bot à distance sympathique :)

Voilà voilà

Tags : , , ,