Symfony : Modifier un formulaire en fonction de son objet

Lors de l’utilisation des formulaire avec Symfony, il est parfois intéressant de modifier le formulaire en fonction de l’objet l’ayant initialisé (une entité en général). Ci dessous quelques exemples permettant d’effectuer ce travail. Les exemples se situe à partir d’une classe de formulaire, en effet à partir du controller nous avons en général directement accès à l’objet d’initialisation.

1) Cas classique

1
2
3
4
5
6
7
8
9
public function buildForm(FormBuilderInterface $builder, array $options) {
    $disabled = false;
    $obj = $builder->getData();//récupération de l'objet
    if ($obj && $obj->getActivated()) {//si la propriete "activated" est true alors certains champs ne seront pas modifiable
        $disabled = true;
    }
    //le champs "field" sera activé ou désactivé selon la propriété précédente
    $builder->add('field', null, array('disabled' => $disabled));
}

On peut aussi directement désactivé entièrement le formulaire de cette manière : $builder->setDisabled(true);

2) Dans un formulaire imbriqué
La technique précédente ne fonctionne pas si l’on est dans un formulaire imbriqué, il faut alors utiliser le code suivant :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
//...
public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use($options) {
        $obj = $event->getData(); //on récupère l'objet
        $form = $event->getForm(); //et le formulaire
        //liste par defaut
        $choicesList = array('ESPÈCES' => 0, 'CHÈQUE' => 1, 'PAIEMENT EN LIGNE' => 2, 'VIREMENT' => 3);
        //si la propriété "type" de l'objet est à 2 on change la liste de proposition
        if ($obj && ($obj->getType() == 2)) {
            $choicesList = array('PAIEMENT EN LIGNE' => 2, 'VIREMENT' => 3);
        }
        //la liste proposé sera différente selon la valeur de la propriété précédente
        $form->add('list', ChoiceType::class, array('choices' => $choicesList));
    });
}

Installer Symfony 3 sous windows (pour l’utiliser sous wamp)

Ceci est un article afin de remplacer le précédent (installation Symfony 2) qui commence à dater.

INSTALLATION SYMFONY (wamp étant considéré comme installé.)

1) Installation de PHP
Ouvrir l’invite de commande, et taper php puis entrée
Si php n’est pas reconnu l’ajouter au PATH :
(Windows 10 et Windows 8)
– Dans Rechercher, lancez une recherche et sélectionnez : Système (Panneau de configuration)
– Cliquez sur le lien Paramètres système avancés.
– Cliquez sur Variables d’environnement. Dans la section Variables système, recherchez la variable d’environnement PATH et sélectionnez-la. Cliquez sur Modifier.
– Dans la fenêtre Modifier la variable système (ou Nouvelle variable système), indiquez la valeur [C:\wamp\bin\php\php5.6.19] (à adapter selon la version php de wamp)
– Cliquez sur OK. Fermez toutes les fenêtres restantes en cliquant sur OK.
– Redémarrer invite de commande.

2) Installer composer
– Utiliser l’installateur pour windows disponible au téléchargent sur le site de composer
Invite de commande :
– Vérifier version : composer.phar –version
– Mettre à jour : composer.phar self-update

3) Installer Git
– Télécharger et exécuter msysgit

4) Installer symfony
Invite de commande :
se placer en invite de commande dans le dossier ou installer… l’installeur (« C:\wamp\www » dans notre cas).
cd C:\wamp\www
php -r « readfile(‘https://symfony.com/installer’); » > symfony

Pour créer votre premier projet symfony, lancer la commande :
php symfony new mon_projet_test

Si vous avez l’erreur suivante :
[GuzzleHttp\Exception\RequestException]
cURL error 60: SSL certificate problem: unable to get local issuer certificate

Télécharger ce certificat cacert.pem
Placer dans votre répertoire C:\wamp (par exemple)
Et modifier votre fichier php.ini (dans « C:\wamp\bin\php\php5.6.19 » pour notre cas)
remplacer la ligne :
;curl.cainfo =
par
curl.cainfo = « C:/wamp/cacert.pem »

Vous pouvez ensuite relancer la commande de nouveau projet symfony (à partir de « C:\wamp\www ») :
php symfony new mon_projet_test

Voir :
http://symfony.com/doc/current/setup.html
https://openclassrooms.com/forum/sujet/symfony-3-erreur-installation

Javascript : Ne pas faire confiance aux calculs avec des nombres à virgule (float)

Cet article fait suite à l’article PHP : Ne pas faire confiance aux calculs avec des nombres à virgule (float), je vous invite à le consulter pour comprendre de quoi on parle.

Les soucis de calculs avec les nombres à virgule étant inhérent au système binaire, on peut reproduire les problèmes soulevés pour PHP en javascript. Ce sera le cas des exemples ci dessous.

Dans le code ci dessous, on essaye d’atteindre un nombre (5.25) en partant de zéro, incrémentant de 0.01

1
2
3
4
5
var nombre = 5.25;
var somme = 0;
while (somme != nombre) {//on ajoute 0.01 tant que notre somme est différente de 5.25
    somme += 0.01;
}//boucle infini

En testant ce code on tombe sur une boucle infini. En effet contrairement à ceux qui est attendu nous n’arrivons jamais à une égalité entre « nombre » et « somme ». On peut comprendre avec le code suivant qui incrémente de 0.01 uniquement tant que « somme » est inférieur ou égale à « nombre ».

1
2
3
4
5
6
var nombre = 5.25;
var somme = 0;
while (somme <= nombre) {//on ajoute 0.01 tant que notre somme est différente de 5.25
    somme += 0.01;
}
document.write(somme);//5.259999999999932

Le résultat donnée (5.259999999999932) est intéressant car même si on voulait arrondir « Math.round(somme*100)/100 » pour éviter le problème, nous obtenons 0.26 au lieu de 0.25, c’est donc un résultat incohérent et il ne faut pas faire ça.

Un autre exemple de calcul qui devrait donner une égalité. Comme ce n’est pas le cas si on compare les 2 valeurs on nous indique qu’elle ne sont pas égale.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var total = 5.25;
var somme = 0;
somme += 0.20; //0.20
somme += 2.30; //2.50
somme += 2.16; //4.66;
somme += 0.01; //4.67
somme += 0.01; //4.68
somme += 0.01; //4.69
somme += 0.01; //4.70
somme += 0.01; //4.71
somme += 0.01; //4.72
somme += 0.53; //5.25;
console.log(total); //5.25
console.log(somme);//5.249999999999999
if (total == somme) {
    document.write('le total correspond !');
} else {
    document.write('Le total ne correspond pas !');
}//Le total ne correspond pas !

il faut donc utiliser une autre technique de comparaison, en prenant en compte la différence minimale que l’on permet. ici pour une valeur du type monétaire (2 chiffres après la virgule) « 0.01 ». Le code de comparaison suivant donnera alors le bon résultat.

1
2
3
4
5
if (Math.abs((total - somme)) < 0.01) {
    document.write('le total correspond !');
} else {
    document.write('Le total ne correspond pas !');
}//le total correspond !

PHP : Ne pas faire confiance aux calculs avec des nombres à virgule (float)

En PHP, il faut faire très attention lors des calculs et des comparaisons avec les « float » (nombres à virgule). En effet les résultats ne sont pas toujours exacts, et cela peux conduire à des bug ou des erreurs souvent difficile à déceler.

Il ne s’agit pas en soit d’un problème de PHP mais bien de l’informatique en général car cela concerne le travail en binaire, ce fait implique des contraintes empêchant de stocker exactement certains nombre flottants ! Pour plus de précisions il existe le site suivant floating-point-gui.de (en anglais) et en particulier sa page Basic Answers

Voyons quelques exemples appliqués en PHP, ne donnant pas le résultat attendus. (je vous propose aussi la même demonstration en javascript.)

Dans le code ci dessous, on essaye d’atteindre un nombre (5.25) en partant de zéro, incrémentant de 0.01

1
2
3
4
5
$nombre = 5.25;
$somme = 0;
while ($somme != $nombre) {//on ajoute 0.01 tant que notre somme est différente de 5.25
    $somme += 0.01;
}//boucle infini

En testant ce code on tombe sur une boucle infini. En effet contrairement à ceux qui est attendu nous n’arrivons jamais à une égalité entre $nombre et $somme. On peut comprendre avec le code suivant qui incrémente de 0.01 uniquement tant que $somme est inférieur ou égale à $nombre.

1
2
3
4
5
6
$nombre = 5.25;
$somme = 0;
while ($somme <= $nombre) {//on ajoute 0.01 tant que notre somme est différente de 5.25
    $somme += 0.01;
}
var_dump($somme)//float(5.2599999999999);

Le résultat donnée (5.2599999999999) est intéressant car même si on voulait arrondir « round($somme, 2) » pour éviter le problème, nous obtenons 0.26 au lieu de 0.25, c’est donc un résultat incohérent et il ne faut pas faire ça.

Ici en affichant les valeurs -bien que le résultat soit inattendu- on voit qu’elles sont différentes. Pourtant en procédant à des calculs, des valeurs peuvent s’afficher identiques mais ne pas l’être lors d’une comparaison ! Exemple avec le code suivant.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$total = 5.25;
$somme = 0;
$somme += 0.20; //0.20
$somme += 2.30; //2.50
$somme += 2.16; //4.66;
$somme += 0.01; //4.67
$somme += 0.01; //4.68
$somme += 0.01; //4.69
$somme += 0.01; //4.70
$somme += 0.01; //4.71
$somme += 0.01; //4.72
$somme += 0.53; //5.25;
var_dump($total);//float(5.25)
var_dump($somme);//float(5.25)
if ($total == $somme) {
    echo 'le total correspond !';
} else {
    echo 'Le total ne correspond pas !';
}//Le total ne correspond pas !

Affiche « Le total ne correspond pas ! » ce qui peux faire perdre des cheveux à tout esprit logique. Quand on veux comparer des nombres à virgule, il faut donc utiliser une autre technique de comparaison, en prenant en compte la différence minimale que l’on permet. ici pour une valeur du type monétaire (2 chiffres après la virgule) 0.01

Le code de comparaison suivant donnera alors le bon résultat.

1
2
3
4
5
if (abs(($total - $somme)) < 0.01) {
    echo 'le total correspond !';
} else {
    echo 'Le total ne correspond pas !';
}//le total correspond !

En réalité il est préférable si on est sur du monétaire par exemple, d’effectuer les calcul en nombre entier. c’est à dire en centimes qu’il restera à diviser par 100 pour l’affichage.

Manipuler les dates en PHP

En PHP l’objet DateTime (ou DateTimeImmutable) permet de manipuler les dates aisément. Quelques exemples commentés ci dessous :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//récupérer la date (et heure) du jour
$now = new DateTime();
//travailler avec une date donnée (4 octobre 2016)
$date = new DateTime('2016-10-04');
//modifier l'heure de cette date (18h20)
$date->setTime(18,20,00);
//afficher la date et l'heure au format voulu
echo $date->format('d/m/Y H:i:s');//04/10/2016 18:20:00
echo '<br>';
//ajouter 3 jours
$date->modify('+3 day');
//on récupére le dernier jour du mois de $date
$end = new DateTime($date->format('Y-m-t'));
//on récupére la différence entre ces 2 dates
$interval = $date->diff($end);
//on affiche le nombre de jour entre notre date et la fin du mois
echo $interval->format('%a jours');//23 jours

Pour plus de précisions voir la documentation PHP.

jQuery : Astuces génériques et bibliothèques utiles

1 : Ouvrir ou fermer n’importe quel élément, ciblé par sa classe ou identifiant. Demo

js

1
2
3
    $(document).on("click", ".toggle-target", function () {
        $($(this).data("target")).toggle();
    });

html

1
2
<div class="toggle-target" data-target=".paragraphe1">paragraphe 1</div>
<p class="paragraphe1">bla bla bla bla bla bla ....<p>

2 : Afficher une boite de confirmation affichant un texte paramétré. Demo

js

1
2
3
4
5
6
    $(document).on("click", ".delete-btn", function () {
        if (confirm($(this).data("delete-confirm"))) {
            return true;
        }
        return false;
    });

html

1
2
    <a href="#suppr1" class="btn btn-danger delete-btn" data-delete-confirm="Message Numéro 1">Suppression 1</a>
    <a href="#suppr2" class="btn btn-danger delete-btn" data-delete-confirm="Autre message">Suppression 2</a>

Diverses bibliothèques utiles :

  • bootstrap js popup, onglet, infobulle, slider…
  • bootstrap-datePicker calendrier sélectionneur de date.
  • select2 transforme un élément select en boite de recherche parmi les options disponibles
  • datatable permet de paginer, trier, rechercher..etc dans les colonnes d’un tableau html

Snippet : js/jQuery recupérer un tableau « key -> value » à partir d’un select html

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
//tableau clé valeur à récupérer à partir d'un select html
var listOptions = [];
$("#id_du_select").find('option').each(function () {
    listOptions[$(this).val()] = $(this).text();
});

function initRegime(options) {
    //valeur selon laquelle les options du select doivent êtres modifiées.
    var val = $("#id_input_de_reference").val();
    if (val == '9000') {
        for (var key in options) {
            //clés à afficher pour cette valeurs.
            if (key == "SS" || key == "SA" || key == "LX") {
                $("#app_quotation_regime").append('<option value="' + key + '">' + options[key] + '</option>');
            }
        }
    } else {
        //par defaut on affiche tous les élements.
        for (var key in options) {
            $("#id_du_select").append('<option value="' + key + '">' + options[key] + '</option>');
        }
    }
}
//appel de la fonction
initRegime(regimeOptions);

Image qui ne s’affiche pas sous firefox (masqué par une class css bizarre)

Voici une explication à un problème qui pourras vous surprendre lors d’un développement.
Si vous avez des images ne s’affichant pas sous firefox, et qu’en inspectant l’élément vous trouvez une classe incongrue s’y étant ajouté du type « class=’yrbrjeoidciaiotneclu' »

Avec une règle correspondante :

1
2
3
.yrbrjeoidciaiotneclu {
    -moz-binding: url("chrome://global/content/bindings/general.xml#foobarbazdummy") !important;
}

Le problème vient en fait d’adblock plus qui prend votre image pour une publicité et décide de la masquer.
Dans mon cas cela se produisait sur une image dont le nom terminait par une taille. du type « nom_de_limage_650x350.jpg » ça semble être le cas sur pas mal de bannière publicitaire d’où le blocage.

On peux donc facilement contourner le problème en changeant le nom de l’image. Et si l’on veux quand même avoir la taille, (pour une génération dynamique par exemple) on peut contourné le blocage en remplacement « 650×350 » par « 650t350 » par exemple.

Symfony : Boucler sur les champs d’un formulaire avec twig

Sous symfony, il est très simple d’afficher un formulaire entier avec twig. Il suffit d’une ligne {{form(form)}}
Il y’a aussi différentes possibilités pour personnaliser l’affichage (afficher les champs un par un, surcharger les template de formulaires, créer des champs personnalisés…etc

Cependant si on veut adapter seulement un champs particulier de notre formulaire, pour y afficher par exemple une explication au dessus ou quelque chose de plus complexe.
La solution la plus rapide est la plus simple est de boucler sur les champs du formulaire et de placer une condition comme ci-dessous :

1
2
3
4
5
6
7
8
9
10
11
{{ form_start(form, {'attr': {'class': 'form-class'}}) }}
    {% for key, field in form.children %}
        {# condition pour afficher le champ particulier #}
       {% if key=='mon_champ_particulier' %}
            <div class="ma_classe">Mon explication, widget ou tout autre éléments spécifique pour ce champ.</div>
            {{form_row(field)}}
        {% else %}
            {{form_row(field)}}
        {% endif %}
    {% endfor %}    
{{ form_end(form) }}

Comme indiqué au dessus, cette solution est à appliqué pour les cas particuliers, pour des éléments semblables qui se répété mieux vaut mettre en place une solution générique avec un type de champ personnalisé par exemple.