Tag: Symfony

Déployer votre projet Symfony en production via rsync

applications-other Vous venez de boucler votre projet symfony en local et êtes impatients de le tester sur votre hébergement réel. Personnellement cette opération ne m’enchante jamais car les lettres FTP sonnent comme une évidence, or jamais un client FTP ne m’aura convaincu surtout quand un jour on a pu goûter au rsync.

Lire la suite

Symfony: Automatiser la navigation dans votre admin-generator

accessories-text-editor Petite reprise d’activité du blog après quelques journée assez chargées. Et me voilà vous proposer une pré alpha d’un petit snippet pour symfony 1.2 qui vise à automatiser la génération d’un menu pour l’admin générator qui malheureusement ne propose rien de semblable à l’heure actuelle.
Et combien il devient vite barbant de rajouter les liens de vos nouveaux modules à la main dans votre layout.

Voici donc une solution possible et facile à mettre en place en se basant sur le fichier de routing.

Lire la suite

Prenez le contrôle de votre CSS

applications-system Votre projet est en plein sprint, les releases de corrections s’enchainent et vous devez soumettre le résultat à chaque fois à votre client. Et pourtant il y a un mécanisme des navigateurs qu’on adore en tant qu’utilisateur mais qui gène parfois en tant que développeur, la mise en cache des CSS.

Quoi de plus frustrant d’avoir en premier retour de votre client Euh, pourquoi l’image est au milieu de l’écran? alors que vous venez de passer les dernières heures à la mettre au bon endroit en retouchant votre CSS. Bien sûr vous lancez rapidement le cri de guerre adéquat F5! et la magie opère enfin.

Mais ne serait-il pas plus plaisant que d’obtenir dès le premier clic, le bon résultat?
Il y a pour cela une astuce, que bon nombre connaisse, mais qui dans un contexte de développement professionnel, est souvent lourde à mettre en place: changer le nom de votre CSS pour forcer le navigateur à la recharger et ainsi laisser apparaitre vos modifications du premier coup.

Lire la suite

Uploadez en Symfony

accessories-text-editor Quand on utilise un framework PHP aussi propre et bien pensé que symfony, on a souvent envie de continuer ce beau travail en ajoutant notre code de la meilleur des façons pour conserver la logique de l’application, MVC, héritage, composition…

Mais ce n’est pas toujours simple. Voici un petit exemple qui concerne un passage obscur de la documentation officielle des formulaires dans symfony, l’upload de fichier.

Pour commencer, il faut souvent définir dans la définition de notre formulaire le widget et le validator correspondant. Dans notre exemple, on prendra un champ nommé avatar, imaginons un classe Profil:

public function configure()
{
    $this->widgetSchema['avatar'] = new sfWidgetFormInputFile();
    // Un upload étant rarement obligatoire, 
   // le cas de l'avatar est un bon exemple, on le rend facultatif ici
    $this->validatorSchema['avatar'] = new sfValidatorFile(array('required' => false)) ; 
}

Maintenant on a bien notre formulaire qui s’affiche correctement. Le problème, si on n’upload pas de nouvel avatar, à la prochaine sauvegarde, le champ avatar sera remis à sa valeur par défaut, fonctionnement tout à fait normal en l’état.
Pour éviter ceci, voici la fonction à rajouter, toujours dans notre classe de notre formulaire Profil, c’est en fait une surcharge de la méthode save:

public function save($con = null)
{
    // On test si le champ avatar a été renseigné dans le formulaire
    if ($file = $this->getValue('avatar'))
    {
      // Si oui, on sauvegarde le fichier
      $filename = 'nomDuFichier';
      $extension = $file->getExtension($file->getOriginalExtension());
      $file->save(sfConfig::get('sf_upload_dir').$filename.$extension);
    }
    else
    {
      // Si non, on récupère la version actuelle dans l'objet Père (ici profil), 
      // que l'on insère dans le tableau de valeur du formulaire
      $this->values['avatar'] = $this->getObject()->getAvatar();
    }
 
    // Puis on lance la sauvegarde normal de la classe mère
    return parent::save($con);
}

Et voilà, nous sommes restés dans la logique métier de symfony, en surchargeant l’existant et surtout en permettant de garder notre action identique, les modifications ayant été apportées dans le modèle.

PHVsPjxsaT48c3Ryb25nPndvb19hZHNfcm90YXRlPC9zdHJvbmc+IC0gdHJ1ZTwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX2ltYWdlXzE8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb20vYWRzL3dvb3RoZW1lcy0xMjV4MTI1LTEuZ2lmPC9saT48bGk+PHN0cm9uZz53b29fYWRfaW1hZ2VfMjwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbS9hZHMvd29vdGhlbWVzLTEyNXgxMjUtMi5naWY8L2xpPjxsaT48c3Ryb25nPndvb19hZF9pbWFnZV8zPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tL2Fkcy93b290aGVtZXMtMTI1eDEyNS0zLmdpZjwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX2ltYWdlXzQ8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb20vYWRzL3dvb3RoZW1lcy0xMjV4MTI1LTQuZ2lmPC9saT48bGk+PHN0cm9uZz53b29fYWRfdXJsXzE8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb208L2xpPjxsaT48c3Ryb25nPndvb19hZF91cmxfMjwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbTwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX3VybF8zPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tPC9saT48bGk+PHN0cm9uZz53b29fYWRfdXJsXzQ8L3N0cm9uZz4gLSBodHRwOi8vd3d3Lndvb3RoZW1lcy5jb208L2xpPjxsaT48c3Ryb25nPndvb19hbHRfc3R5bGVzaGVldDwvc3Ryb25nPiAtIGN1c3RvbS5jc3M8L2xpPjxsaT48c3Ryb25nPndvb19hdXRvX2ltZzwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fY29udGVudF9hcmNoaXZlczwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fY29udGVudF9ob21lPC9zdHJvbmc+IC0gZmFsc2U8L2xpPjxsaT48c3Ryb25nPndvb19jdXN0b21fZmF2aWNvbjwvc3Ryb25nPiAtIDwvbGk+PGxpPjxzdHJvbmc+d29vX2ZlZWRidXJuZXJfdXJsPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fZ29vZ2xlX2FuYWx5dGljczwvc3Ryb25nPiAtIDwvbGk+PGxpPjxzdHJvbmc+d29vX2xvZ288L3N0cm9uZz4gLSBodHRwOi8vd3d3LmFtaWNhbGVtZW50LXdlYi5uZXQvd3AtY29udGVudC93b29fdXBsb2Fkcy8zLWxvZ28ucG5nPC9saT48bGk+PHN0cm9uZz53b29fbWFudWFsPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tLzwvbGk+PGxpPjxzdHJvbmc+d29vX25hdl9leGNsdWRlPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fcmVzaXplPC9zdHJvbmc+IC0gdHJ1ZTwvbGk+PGxpPjxzdHJvbmc+d29vX3Nob3J0bmFtZTwvc3Ryb25nPiAtIHdvbzwvbGk+PGxpPjxzdHJvbmc+d29vX3RoZW1lbmFtZTwvc3Ryb25nPiAtIFR5cGViYXNlZDwvbGk+PGxpPjxzdHJvbmc+d29vX3RodW1iX2hlaWdodDwvc3Ryb25nPiAtIDEwMDwvbGk+PGxpPjxzdHJvbmc+d29vX3RodW1iX3dpZHRoPC9zdHJvbmc+IC0gMTAwPC9saT48bGk+PHN0cm9uZz53b29fdXBsb2Fkczwvc3Ryb25nPiAtIGh0dHA6Ly93d3cuYW1pY2FsZW1lbnQtd2ViLm5ldC93cC1jb250ZW50L3dvb191cGxvYWRzLzMtbG9nby5wbmc8L2xpPjwvdWw+