[Widget] sfWidgetFormTimeAndClear

Après trois semaines de vacances au soleil et quelques semaines limitées sur le plan du dev, me voici de retour pour votre plus grand plaisir :o

Encore une fois, je vous propose un petit widget légèrement personnalisé : sfWidgetFormTimeAndClear.

On continue dans la lancée du sfWidgetFormDoctrineJQueryAutocompleterAndClear avec le même petit bouton permettant de vider le contenu des listes déroulantes du champ d’heure.

Il faut bien sûr intégrer jQuery pour que cela fonctionne mais vous l’aviez sans doute compris :)

Voici le code :

class sfWidgetFormTimeAndClear extends sfWidgetFormTime
{
  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    return parent::render($name, $value, $attributes, $errors).
           ' <img src="data:image/png;base64,'.$this->getImage().'" alt="" id="'.$this->getDeleteId($name).'" />'.
           sprintf(<<<EOF
<script type="text/javascript">
  $("#%s").bind("mouseover", function(e){
    $("#%s")[0].style.cursor = "pointer";
  });
  $("#%s").bind("click", function(e){
    $("#%s")[0].value = "";
    $("#%s")[0].value = "";
    %s
    return false;
  });

</script>
EOF
      ,
      $this->getDeleteId($name),
      $this->getDeleteId($name),
      $this->getDeleteId($name),
      $this->getHourId($name),
      $this->getMinuteId($name),
      $this->getSecondId($name)
    );
  }

  private function getHourId($value)
  {
    return $this->generateId($value."_hour");
  }

  private function getMinuteId($value)
  {
    return $this->generateId($value."_minute");
  }

  private function getSecondId($value)
  {
    if ($this->getOption('with_seconds'))
    {
      return '$("#'.$this->generateId($value."_second").'")[0].value = "";';
    }
    return '';
  }

  private function getDeleteId($value)
  {
    return $this->generateId("delete_hour_value_".$value);
  }

  private function getImage()
  {
    return "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==";
  }
}

Et voici à quoi cela ressemble :

Je vais sans doute proposer également les widgets date et datetime sous ce format dans les jours à venir.

[Widget] sfWidgetFormDoctrineJQueryAutocompleterAndClear

Pour faire suite à mon billet sur le widget sfWidgetFormDoctrineJQueryAutocompleter, je vous propose mon petit widget sfWidgetFormDoctrineJQueryAutocompleterAndClear.

Il propose un bouton permettant de vider le contenu du champ texte mais également du champ caché d’autocompletion.

Il fonctionne exactement de la même manière que le widget original, il ne faut rien de plus et rien de moins (l’icône est intégrée en base64).

Voici le code :

class sfWidgetFormDoctrineJQueryAutocompleterAndClear extends sfWidgetFormDoctrineJQueryAutocompleter
{
  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    return parent::render($name, $value, $attributes, $errors).
           ' <img src="data:image/png;base64,'.$this->getImage().'" alt="" id="'.$this->getDeleteId($name).'" />'.
           sprintf(<<<EOF
<script type="text/javascript">
  $("#%s").bind("mouseover", function(e){
    $("#%s")[0].style.cursor = "pointer";
  });
  $("#%s").bind("click", function(e){
    $("#%s")[0].value = "";
    $("#%s")[0].value = "";
    return false;
  });

</script>
EOF
      ,
      $this->getDeleteId($name),
      $this->getDeleteId($name),
      $this->getDeleteId($name),
      $this->generateId('autocomplete_'.$name),
      $this->generateId($name)
    );
  }

  private function getDeleteId($value)
  {
    return $this->generateId("delete_autocomplete_value_".$value);
  }

  private function getImage()
  {
    return "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJdSURBVDjLpZP7S1NhGMf9W7YfogSJboSEUVCY8zJ31trcps6zTI9bLGJpjp1hmkGNxVz4Q6ildtXKXzJNbJRaRmrXoeWx8tJOTWptnrNryre5YCYuI3rh+8vL+/m8PA/PkwIg5X+y5mJWrxfOUBXm91QZM6UluUmthntHqplxUml2lciF6wrmdHriI0Wx3xw2hAediLwZRWRkCPzdDswaSvGqkGCfq8VEUsEyPF1O8Qu3O7A09RbRvjuIttsRbT6HHzebsDjcB4/JgFFlNv9MnkmsEszodIIY7Oaut2OJcSF68Qx8dgv8tmqEL1gQaaARtp5A+N4NzB0lMXxon/uxbI8gIYjB9HytGYuusfiPIQcN71kjgnW6VeFOkgh3XcHLvAwMSDPohOADdYQJdF1FtLMZPmslvhZJk2ahkgRvq4HHUoWHRDqTEDDl2mDkfheiDgt8pw340/EocuClCuFvboQzb0cwIZgki4KhzlaE6w0InipbVzBfqoK/qRH94i0rgokSFeO11iBkp8EdV8cfJo0yD75aE2ZNRvSJ0lZKcBXLaUYmQrCzDT6tDN5SyRqYlWeDLZAg0H4JQ+Jt6M3atNLE10VSwQsN4Z6r0CBwqzXesHmV+BeoyAUri8EyMfi2FowXS5dhd7doo2DVII0V5BAjigP89GEVAtda8b2ehodU4rNaAW+dGfzlFkyo89GTlcrHYCLpKD+V7yeeHNzLjkp24Uu1Ed6G8/F8qjqGRzlbl2H2dzjpMg1KdwsHxOlmJ7GTeZC/nesXbeZ6c9OYnuxUc3fmBuFft/Ff8xMd0s65SXIb/gAAAABJRU5ErkJggg==";
  }
}

Et à quoi cela ressemble au final :

sfWidgetFormTextareaTinyMCEWithLang

Je souhaitais vous faire partager mon petit widget TinyMCE basé sur celui intégré au plugin sfFormExtraPlugin.

Pourquoi réécrire un widget existant ? Parce que celui-ci n’acceptait pas le paramètre de langue !

Quelque part, il s’agit plus d’un correctif du widget mais bon, il s’avère que j’en ai souvent besoin donc je le partage ici :

class sfWidgetFormTextareaTinyMCEWithLang extends sfWidgetFormTextarea
{
  protected function configure($options = array(), $attributes = array())
  {
    $this->addOption('theme', 'advanced');
    $this->addOption('language', 'en');
    $this->addOption('width');
    $this->addOption('height');
    $this->addOption('config', '');
  }

  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    $textarea = parent::render($name, $value, $attributes, $errors);

    $js = sprintf(<<<EOF
<script type="text/javascript">
  tinyMCE.init({
    mode:                              "exact",
    elements:                          "%s",
    language:                          "%s",
    theme:                             "%s",
    %s
    %s
    theme_advanced_toolbar_location:   "top",
    theme_advanced_toolbar_align:      "left",
    theme_advanced_statusbar_location: "bottom",
    theme_advanced_resizing:           true
    %s
  });
</script>
EOF
    ,
      $this->generateId($name),
      $this->getOption('language'),
      $this->getOption('theme'),
      $this->getOption('width')  ? sprintf('width:                             "%spx",', $this->getOption('width')) : '',
      $this->getOption('height') ? sprintf('height:                            "%spx",', $this->getOption('height')) : '',
      $this->getOption('config') ? ",\n".$this->getOption('config') : ''
    );

    return $textarea.$js;
  }
}

Et pour l’utiliser :

  • dans votre classe de formulaire
$this->widgetSchema['widget'] = new sfWidgetFormTextareaTinyMCEWithLang(sfConfig::get('app_tinymce_config'));
  • dans le fichier app.yml
all:
  tinymce:
    width:  550
    height: 125
    language: fr
    config: |
      plugins: "paste",
      paste_auto_cleanup_on_paste : true,
      theme_advanced_buttons1: "bullist,pasteword,bold,italic,underline,fontselect,fontsizeselect",
      theme_advanced_buttons2: "",
      theme_advanced_buttons3: "",
      theme_advanced_buttons4: ""
    theme: <?php echo sfConfig::get('app_tinymce_theme','advanced') ?>

Edit : un billet sur le même sujet existe en anglais (merci à Tomasz Ducin)

[Admin generator] Paramètre ‘table method’

Une petite astuce que j’utilise depuis longtemps mais que je n’avais jamais pensé à partager : optimiser le nombre de requêtes Doctrine et par la même occasion, un module d’admin generator.

J’ai un module d’admin generator article lié au modèle Article.

  • schema.yml
Article:
  connection: doctrine
  actAs:
    Timestampable: ~
    Taggable: ~
    Sluggable:
      fields: [titre]
      name: slug
      type: string
      length: 255
      unique: true
      canUpdate: true
  columns:
    titre:
      type: string(255)
      notnull: true
    # et d'autres champs
    id_auteur:
      type: integer
      notnull: true
  relations:
    Auteur:  { class: vjGuardUserProfile, onDelete: SET NULL, onUpdate: CASCADE, local: id_auteur, foreign: id }
  • generator.yml
 list:
 title: Liste des articles
 display: [titre, date_publication, Auteur]
 max_per_page: 5
 table_method: retrieveBackend
  • ArticleTable.class.php
public function retrieveBackend(Doctrine_Query $q)
{
  $rootAlias = $q->getRootAlias($q);
  $q->leftJoin($rootAlias.'.Auteur au');
  return $q;
}

Dans cet exemple, je passe de 6 requêtes Doctrine pour l’affichage de 5 lignes à 3 requêtes.

Ce n’est pas énorme mais sur 20 lignes et 3 relations, on passe de 63 requêtes à

3 requêtes !

Sisi !

Alors, abusez-en !

MYSQL – Importer des données en latin dans une base en utf8

Une petite astuce que je viens de découvrir et qui me sauve la vie !

Problème : J’ai une base en latin1 et une réplication en utf8. Comment migrer les données de l’une vers l’autre en ligne de commande (sur deux serveurs de version totalement différentes) ?

Solution :

# on dump la base initiale en latin1
mysqldump -h host1 -u login -p'password' database > save.sql
# on importe les données dans la nouvelle base en utf8
mysql -h host2 -u login -p'password' --default_character_set utf8 database < save.sql

Petit mais costaud !

MYSQL – Modifier le character set de tous les champs/tables d’une database

Je pense que comme la plupart des utilisateurs de MySQL, il vous est arrivé de devoir changer l’encodage des caractères d’une base de données, d’une table ou pire, des champs et quel cauchemar …

Je viens de trouver un joli petit script php qui le fait assez proprement. Il suffit de renseigner le character_set initial, celui recherché, les identifiants de connexion à la base de données puis de lancer le script et enfin, d’exécuter les requêtes fournies :

#!/usr/bin/php
<?php

// FICHIER convertCharset.php

// this script will output the queries need to change all fields/tables to a different collation
// it is HIGHLY suggested you take a MySQL dump prior to running any of the generated
// this code is provided as is and without any warranty

  set_time_limit(0);

  // Connexion BDD
	define("USER","username");
	define("PASS",'password');
	define("HOST","your_host_name");
	define("DB",$argv[1]);

  $path_tmp = "/tmp/";
  $mysql_params = "-h ".HOST." -u ".USER." -p'".PASS."'";

  // collation you want to change:
  $convert_from = 'latin1_swedish_ci';
  //$convert_from = 'utf8_unicode_ci';

  // collation you want to change it to:
  $convert_to   = 'utf8_general_ci';

  // character set of new collation:
  $character_set= 'utf8';

  $show_alter_table = true;
  $show_alter_field = true;

  // DB login information

  mysql_connect(HOST, USER, PASS);
  mysql_select_db(DB);

  $rs_tables = mysql_query(" SHOW TABLES ") or die(mysql_error());
  $sql = "";

  while ($row_tables = mysql_fetch_row($rs_tables)) {
      $table = mysql_real_escape_string($row_tables[0]);

      // Alter table collation
      // ALTER TABLE `account` DEFAULT CHARACTER SET utf8
      if ($show_alter_table) {
          $sql.= "ALTER TABLE `$table` DEFAULT CHARACTER SET $character_set;\r\n";
      }

      $rs = mysql_query(" SHOW FULL FIELDS FROM `$table` ") or die(mysql_error());
      while ($row=mysql_fetch_assoc($rs)) {

          if ($row['Collation']!=$convert_from)
              continue;

          // Is the field allowed to be null?
          if ($row['Null']=='YES') {
              $nullable = ' NULL ';
          } else {
              $nullable = ' NOT NULL';
          }

          // Does the field default to null, a string, or nothing?
          if ($row['Default']=='NULL') {
              $default = " DEFAULT NULL";
          } else if ($row['Default']!='') {
              $default = " DEFAULT '".mysql_real_escape_string($row['Default'])."'";
          } else {
              $default = '';
          }

          // Alter field collation:
          // ALTER TABLE `account` CHANGE `email` `email` VARCHAR( 50 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
          if ($show_alter_field) {
              $field = mysql_real_escape_string($row['Field']);
              $sql.= "ALTER TABLE `$table` CHANGE `$field` `$field` $row[Type] CHARACTER SET $character_set COLLATE $convert_to $nullable $default; \r\n";
          }
      }
  }

system("mysqldump $mysql_params ".DB." > ".$path_tmp."before-conv-charset-".DB.".sql");
$fp = fopen($path_tmp.'conv-charset-'.DB.'.sql', 'w');
fwrite($fp, $sql);
fclose($fp);
system("mysql $mysql_params ".DB." < ".$path_tmp."conv-charset-".DB.".sql");
?>

Il ne reste plus qu’à exécuter le script :

php convertCharset.php DATABASE_NAME

Magie !

Pourquoi optimiser les requêtes Doctrine ?

Bonjour à tous !

Et oui, me revoilà après plusieurs mois sans post. Il faut dire qu’entre le boulot, les cours du soir et le sport, le temps est assez limité pour le reste ! Mais bon, je profite d’avoir un petit moment pour vous faire partager une expérience qui, pour beaucoup, semble évidente mais à laquelle on ne pense pas suffisament : Optimiser une requête Doctrine

Dans l’admin generator, on pense souvent à modifier la requête de sélection de Doctrine (pour l’affichage de la liste) en intégrant des leftJoin ou innerJoin (ceux qui ne le font pas encore, pensez-y !).

Je le fais toujours mais dans mes modules de frontend (en général, de l’affichage de données en base), je ne le faisais pas (sans doute parce que mes requête étaient simples).
Sur un projet, j’ai pu découvrir le bénéfice non négligeable d’optimiser la requête d’affichage des données, que ce soit au niveau du nombre de requêtes mais également au niveau du temps d’affichage de la page (plus précisément, au niveau du temps de calcul de l’actin d’index).

Pour que ce soit plus parlant, regardons quelques printscreen :
- utilisation de la requête générée par Doctrine

Requête non optimisée

- utilisation d’une requête (et d’une action) optimisée

Voilà, je pense que c’est suffisamment parlant non ?
Pour information, j’ai rafraichi plusieurs fois la page afin d’obtenir des valeurs lorsque le cache est utilisé.

En non optimisé, Doctrine effectue 129 requêtes (3.98ms) et une fois optimisée, UNE seule requête est effectuée (0.01ms).
Le temps étant en ms, c’est assez négligeable certes mais pour une base de données énorme avec beaucoup d’accès, le ratio est de 400 ! De plus, le fait d’avoir un certain nombre de requêtes (hydratation de base Objet) rentre en compte dans le temps de calcul de l’action dont elle dépend et ici, le ratio est encore important 6 !

Afin de vous ouvrir un peu plus les yeux, il faut voir le schema de la base afin de comprendre la « complexité » des relations :

#schema.yml
Etat:
  tableName: etat
  columns:
    titre:
      type: string(255)
      notnull: true
    color:
      type: string(255)
      notnull: true
Comment:
  actAs:
    Timestampable: ~
  tableName: comment
  columns:
    titre:
      type: string(255)
    comment:
      type: clob
      notnull: true
    user_id:
      type: integer(4)
  relations:
    sfGuardUser:
      type: one
      foreignType: many
      local: user_id
      foreign: id
      foreignAlias: Users
Tache:
  actAs:
    Timestampable: ~
  tableName: tache
  columns:
    activite:
      type: string(255)
      notnull: true
    id_etat:
      type: integer
      notnull: true
  relations:
    Etat:  { onDelete: CASCADE, local: id_etat, foreign: id }
    TacheComments:
      class: Comment
      refClass: TacheComment
      local: id_tache
      foreign: id_comment
    sfGuardUsers:
      class: sfGuardUser
      refClass: TacheUser
      local: id_tache
      foreign: id_user
TacheUser:
  tableName: tache_user
  columns:
    id_tache:
      type: integer
      notnull: true
    id_user:
      type: integer(4)
      notnull: true
  relations:
    Tache:  { onDelete: CASCADE, local: id_tache, foreign: id }
    sfGuardUser: { onDelete: CASCADE, local: id_user, foreign: id }
TacheComment:
  tableName: tache_comment
  columns:
    id_tache:
      type: integer
      notnull: true
    id_comment:
      type: integer
      notnull: true
  relations:
    Tache:  { onDelete: CASCADE, local: id_tache, foreign: id }
    Comment: { onDelete: CASCADE, local: id_comment, foreign: id }

Voici également la requête optimisée (je ne sélectionne que les données nécessaires et j’hydrate en array afin de limite les ressources nécessaires et sachant qu’il s’agit d’affichage de données, c’est parfait !) :

// TacheTable.class.php
  public function retrieveTacheWithJoins(){
    return Doctrine_Query::create()
          ->select('
              t.*,
              e.*,
              tc.id_comment,
              tu.id_user,
              c.titre, c.comment, c.user_id, c.updated_at,
              cu.username,
              u.username
            ')
          ->from('Tache t')
          ->leftJoin('t.Etat e')
          ->leftJoin('t.TacheComment tc')
          ->leftJoin('tc.Comment c')
          ->leftJoin('c.sfGuardUser cu')
          ->leftJoin('t.TacheUser tu')
          ->leftJoin('tu.sfGuardUser u')
          ->execute(array(), Doctrine::HYDRATE_ARRAY);
  }

Et pour finir, je vous propose les deux actions (en premier, l’action optimisée avec l’hydratation array et en second, l’ancienne action avec l’hydratation objet) :

//actions.class.php
  public function executeIndex(sfWebRequest $request)
  {
    $type = $request->getParameter('type');
    $this->etat = $request->getParameter('etat');
    $this->taches = Doctrine::getTable('Tache')->retrieveTacheWithJoins();
    $this->tab = array();
    $this->comments = array();
    foreach($this->taches as $tache){
      $this->users = array();
      foreach($tache['TacheUser'] as $user){
        $this->users[] = $user['sfGuardUser']['username'];
      }
      $this->users = implode(" ", $this->users);
      $this->tab[$tache['id']] = array(
                                        'activite' => $tache['activite'],
                                        'etat' => $tache['Etat']['titre'],
                                        'etat_color' => $tache['Etat']['color'],
                                        'users' => $this->users,
                                    );

      foreach($tache['TacheComment'] as $comment){
        $this->comments[$tache['id']][] = array(
                                              'user' => $comment['Comment']['sfGuardUser']['username'],
                                              'titre' => $comment['Comment']['titre'],
                                              'date' => date('d/m/Y', strtotime($comment['Comment']['updated_at'])),
                                              'comment' => $comment['Comment']['comment'],
                                            );
      }
    }
    $this->pdf = new pdf('L');
    $this->setTemplate($type);
  }

  public function executeOldIndex(sfWebRequest $request)
  {
    $type = $request->getParameter('type');
    $this->etat = $request->getParameter('etat');
    $this->taches = Doctrine::getTable('Tache')->findAll();
    $this->tab = array();
    $this->comments = array();
    foreach($this->taches as $tache){
      $this->users = array();
      foreach($tache->TacheUser as $user){
        $this->users[] = $user->getSfGuardUser();
      }
      $this->users = implode(" ", $this->users);
      $this->tab[$tache->getId()] = array(
                                        'activite' => $tache->getActivite(),
                                        'etat' => $tache->getEtat(),
                                        'etat_color' => $tache->getEtat()->getColor(),
                                        'users' => $this->users,
                                    );

      foreach($tache->TacheComment as $comment){
        $this->comments[$tache->getId()][] = array(
                                              'user' => $comment->getComment()->getSfGuardUser(),
                                              'titre' => $comment->getComment()->getTitre(),
                                              'date' => date('d/m/Y', strtotime($comment->getComment()->getUpdatedAt())),
                                              'comment' => $comment->getComment()->getComment(),
                                            );
      }
    }
    $this->pdf = new pdf('L');
    $this->setTemplate($type);
  }

Voilà, je pense que c’est assez explicite, j’obtiens exactement le même rendu avec l’une ou l’autre des actions et des requêtes et j’avoue avoir été vraiment impressionné sur ce coup là !
Morale : toujours chercher à optimiser ses requêtes !

De la regex …

… il en faut et dieu sait que c’est pas simple à utiliser quand on ne le fait que rarement.

Il arrive que dans symfony, on en ait besoin (pourtant, symfony fournit un paquet de validateurs efficaces – comme pour le mail) et quand on est pas coutumier de leur utilisation, aie aie aie !

Bref, j’ai trouvé un petit site sympathique avec plein d’exemples (des sites expliquant les regex, yen a plein mais des exemples concrets, j’ai toujours du mal à en trouver).
Je vous le fais donc partager : http://www.roscripts.com/PHP_regular_expressions_examples-136.html

Et juste pour la forme, un petit pattern qui peut servir à tester des champs avec des accents ou des ‘ç’ par exemple :

'/^[\p{L}\-\' \*]{1,255}$/'

Comment activer le ‘no_script_name’ de symfony

Afin de ne pas avoir index.php dans l’url réécrite de votre site en production, il y a deux manipulations à effectuer :

- activer le no_script_name :

.settings:
  no_script_name: on

- activer le mod_rewrite d’apache :

a2enmod rewrite
/etc/init.d/apache2 restart

N’oubliez pas non plus de modifier la configuration de votre site (/etc/apache2/conf/xxx.conf) en indiquant index.php pour l’application de base de votre site.

getParameterHolder()->set() = setParameter()

$request->getParameterHolder()->set('foo', 'bar');
// équivaut à
$request->setParameter('foo', 'bar');
$request->getParameterHolder()->get('foo');
// équivaut à
$request->getParameter('foo');

Vous pouvez passer un paramètre par défaut dans le getter si le paramètre foo n’existe pas :

$request->getParameter('foobar', 'default');

Je n’invente rien, il s’agit juste d’une astuce fournie dans le guide de symfony mais à côté de laquelle on peut passer :)