Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Membre du Club
    Format de date entre assert de l’entité et champ du formulaire avec un datepicker
    Bonjour,

    J’ai un problème où je ne vois pas comment faire.
    Pour l’instant, j’ai un code qui marche bien, mais je souhaite l’optimiser en utilisant les asserts dans mon entité.
    Désolé pour le pavé, mais je vais essayer d’être clair...

    Le principe de mon code actuel est le suivant :
    J’ai une entité avec un champ date défini en texttype, sinon le datepicker que j’utilise ne fonctionne pas.
    Avec mon datepicker dans mon formulaire, le format de ma date est jj/mm/ssaa.
    En base, je stock cette date au format ssaa-mm-jj dans un champ en varchar(10).
    Ça, c’est un choix que j’ai fait depuis la création de mon site, et je préférerai ne pas devoir tout changer : les saisies dans les formulaires se font au format jj/mm/ssaa, et les dates sont stockées en base au format ssaa-mm-jj.
    Dans mon contrôleur, après le test isvalid de mon formulaire, je contrôle la date saisie, la converti au format ssaa-mm-jj, et l’insère dans ma bdd.
    Tout ça, ça marche bien !

    Je ne mets pas le code, car tout est sur le principe des docs qu’on trouve sur Symfony.

    Maintenant, je veux optimiser mon code, et utiliser les asserts au niveau de mon entité, pour supprimer les contrôles de date dans mon contrôleur, et mettre dans ma bdd ma date au format Date au lieu de varchar(10).

    Dans mon entité, j’ajoute l’assert suivant sur mon champ date, assert qui prend par défaut le format ssaa-mm-jj, qui est bien celui que je veux pour stocker ma valeur en base :
    @assert\date

    Vu qu’avec mon datepicker la date est au format jj/mm/ssaa, ma valeur est toujours invalide.

    J’ai essayé dans mon contrôleur de changer le format de ma date en ssaa-mm-jj, avant le test isvalid de mon formulaire.
    Et là j’ai un message d’erreur qui me dit qu’on ne peut pas modifier un formulaire qui a déjà été soumis.

    Y a-t-il un moyen de changer le format de ma date quand je soumets mon formulaire ?

    Y a-t-il un fonction de Symfony qui permet ça, et que je ne connais pas ?
    Ou un petit code Jquery (que j’utilise déjà dans mon projet pour d’autres trucs) ? Je ne suis pas du tout un pro de Jquery, donc est ce faisable ? Et comment ?

    Merci d’avance pour votre aide !

  2. #2
    Membre confirmé
    Salut,

    À quoi correspond le format SSAA-MM-JJ ? Est-ce qu'il y a une différence avec le Y-m-d de PHP ?

    Citation Envoyé par merou19 Voir le message
    J’ai une entité avec un champ date défini en texttype, sinon le datepicker que j’utilise ne fonctionne pas.
    Avec mon datepicker dans mon formulaire, le format de ma date est jj/mm/ssaa.
    Quel datepicker utilises-tu ?

    Est-ce que tu ne pourrais pas utiliser un champ DateType, avec des options pour l'affiche sous la forme d'un <input type="text"> ?
    Normalement justement les différents Types de champ permettent de s'occuper de la transformation des données entre celles saisies dans le formulaire et celles définies dans l'objet qui sera ensuite persister (et vice versa quand on récupère des données de la base pour les afficher dans le formulaire de modification par exemple).

    Code php :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    $builder->add('dateCreated', DateType::class, [
        'widget' => 'single_text',
        'html5' => false,
        'attr' => ['class' => 'js-datepicker'],
        'format' => 'dd/MM/yyyy',
        'input' => 'string',
        'input_format' => 'Y-m-d' // ajouté en 4.3
    ]);


    Si je ne me suis pas loupé, ça devrait permettre de saisir des dates au format jj/mm/aaaa qui seront stockées dans l'objet au format Y-m-d.
    Et si c'est le cas, normalement tu n'as plus besoin ni de contrôler le format ni de convertir la valeur dans ton contrôleur (c'est le type de champ qui s'en charge et tes contraintes de validation).

    Si ça ne fonctionne pas essaye de voir si tu ne peux pas faire ce que tu souhaites en croisant les différentes options du DateType : https://symfony.com/doc/current/refe...te.html#format

    Et si tu ne trouves pas ton bonheur, une autre solution consiste à créer un Data Transform Objet (DTO) : https://symfonycasts.com/screencast/...forms/form-dto (vidéo payante mais script gratuit en dessous)

  3. ###raw>post.musername###
    Membre du Club
    YES ! Merci beaucoup, c'est exactement ce qu'il me faut !

    Oui, dans mon premier message, quand je parle de ssaa-mm-jj, je veux dire le format Y-m-d de PHP.

    Pour le datepicker, j'utilise jQuery UI Datepicker 1.12.1

    Bon, maintenant j'ai un probème de type de champ, voilà ce que j'ai fait (j'ai peut être mis la grouille partout) :
    Mes entités avaient été créées sous SF3. Pour mon entité, j'ai changé le champ dateDebut de type="string", length=10 en type="date". Puis j'ai mis à jour ma bdd.
    J'ai modifié mon code comme tu indiques.
    Puis j'ai essayé : message d'erreur, je ne sais plus lequel.
    Je me suis dit, peut être à tort, que ça vient de mes getters et setters de mon entité qui sont restés avec le format string.
    J'ai essayé de régénérer les getters et setters avec la commande de SF3, qui ne marche plus sous SF4.
    J'ai trouvé ici : https://stackoverflow.com/questions/50280135/symfony-4-getter-setter-not-generated-from-existing-database
    2 commandes qui régénérent les entités et getters et setters à partir de la bdd :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    php bin/console doctrine:mapping:import App\\Entity annotation --path=src/Entity
    php bin/console make:entity --regenerate App

    ça m'a écrasé mes asserts, mais ce n'est pas grave, je les remettrai plus tard.

    Et maintenant, en testant après tout ça, en mettant des dates valides, j'ai le message d'erreur :
    Expected argument of type "DateTimeInterface", "string" given at property path "dateDebut".
    à la ligne :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    $form->handleRequest($request);


    Voilà le code généré pour mon champ :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    <div class="form-group"><label for="form_dateDebut" class="required">Date de début</label><input type="text" id="form_dateDebut" name="form[dateDebut]" required="required" class="js-datepicker form-control" /></div>

    J'ai bien le input type="text".

    2 questions svp :
    Après avoir régénéré mes entités, je vois dans leur code qu'il y a dans les getters et setters des points d'interrogation suivis de types. ça n'était pas présent dans mes entités générées sous SF3. Vous savez à quoi ça sert ? C'est important ?
    Vous avez des idées pour mon message d'erreur au handleRequest ?

    Merci encore Thomas pour ton aide, et merci d'avance à tous pour vos réponses !

    Mon entité :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
        /**
         * @var \DateTime
         *
         * @ORM\Column(name="date_debut", type="date", nullable=false)
         */
        private $dateDebut;
     
        public function getDateDebut(): ?\DateTimeInterface
        {
            return $this->dateDebut;
        }
     
        public function setDateDebut(\DateTimeInterface $dateDebut): self
        {
            $this->dateDebut = $dateDebut;
     
            return $this;
        }


    mon contrôleur :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    ->add('dateDebut', DateType::class, [
    	'label' => 'Date de début',
    	'widget' => 'single_text',
    	'html5' => false,
    	'attr' => ['class' => 'js-datepicker'],
    	'format' => 'dd/MM/yyyy',
    	'input' => 'string',
    	'input_format' => 'Y-m-d'
    ])


    [hors sujet] Thomas, je dois être bigleux, je ne vois pas dans les options de mise en forme des messages celle pour surligner un texte, comment as tu fait stp ?
      0  0

  4. #4
    Membre confirmé
    Citation Envoyé par merou19 Voir le message
    Après avoir régénéré mes entités, je vois dans leur code qu'il y a dans les getters et setters des points d'interrogation suivis de types. ça n'était pas présent dans mes entités générées sous SF3. Vous savez à quoi ça sert ? C'est important ?
    Ça fait partie des nouveautés de PHP 7 .1 pour déclarer un type nullable et indiquer qu'on accepter soit null soit un argument du type qui suit le point d'interrogation (voir ici).

    Citation Envoyé par merou19 Voir le message
    [hors sujet] Thomas, je dois être bigleux, je ne vois pas dans les options de mise en forme des messages celle pour surligner un texte, comment as tu fait stp ?
    Il faut utiliser les balises [CODEInline] pour "surligner" avec la mise en forme des balises [CODE] (je l'ai su en citant un message contenant ces balises, car moi non plus je ne comprenais pas comment faire depuis l'éditeur WYSIWYG^^).

    Concernant l'erreur obtenue, vu que le setter attend un argument de type DateTimeInterface, je pense qu'il faut maintenant modifier ton champ de formulaire comme ceci (si je ne comprends pas de travers l'option "input") :
    Code php :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    ->add('dateDebut', DateType::class, [
    	'label' => 'Date de début',
    	'widget' => 'single_text',
    	'html5' => false,
    	'attr' => ['class' => 'js-datepicker'],
    	'format' => 'dd/MM/yyyy',
    	'input' => 'datetime',
    ])

  5. #5
    Membre du Club
    Merci beaucoup pour les infos et les liens !

    Donc, j'ai mis mon input en datetime, et après avoir modifié le code dans mon contrôleur, vu que maintenant j'ai un objet datetime et non une chaîne, ça marche enfin !!!

    Juste un dernier point :
    Dans mon projet, j'affiche une page avec une boucle qui liste tout le contenu de ma base pour mon entité.
    Je peux sélectionner une ligne, et cliquer sur l'un des 4 boutons : voir, modifier, dupliquer, supprimer.
    ça marche bien !

    Quand je vais sur voir, j'affiche mon formulaire en mettant les champs en attribut readonly, ça marche, et je n'ai pas les datepicker.

    Quand je vais sur 'modifier' et 'dupliquer', ça m'affiche mon formulaire renseigné, et je n'ai plus les datepicker, alors que je veux les avoir.

    Pour corriger ça, je veux activer mon datepicker que si je suis en création, modification et duplication.

    Voilà ce que j'ai fait dans ma vue : j'affiche tout mon formulaire par form_widget(form_entite) et pas champ par champ, et j'ai ajouté en fin de code mon script pour activer les datepicker.

    Code twig :Sélectionner tout -Visualiser dans une fenêtre à part
    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
    47
    48
    49
    50
    51
    52
    53
    54
    <div class="row">
    	<div class="col-xl-12">
    		{% if (action is empty) or (action == 'creation_entite') %} <h1>Créer une entite</h1> {% endif %}
    		{% if action == 'voir_entite' %} <h1>Voir la entite</h1> {% endif %}
    		{% if action == 'suppression_entite' %} <h1>Entite à supprimer</h1> {% endif %}
    		{% if action == 'modification_entite' %} <h1>Entite avec données à modifier</h1> {% endif %}
    		{% if action == 'duplication_entite' %} <h1>Entite avec données à dupliquer</h1> {% endif %}
    		<br>
    		{{ form_start(form_entite, {'action': path('admin_entite_creer'), 'method':'POST'}) }}
    			Tous les champs sont obligatoires.<br><br>
    			<div>
    				{{ form_widget(form_entite) }}
    			</div>
    			<br>
    			{% if (action is empty) or (action == 'creation_entite') %}
    				<div class="form-group">
    					<button type="submit" id="submit" value="Créer" name="submit" class="btn-default btn">Créer</button>
    					<button type="submit" id="submit" value="Annuler" name="submit" class="btn-default btn">Annuler</button>
    				</div>
    			{% endif %}
    			{% if action == 'voir_entite' %}
    				<div class="form-group">
    					<button type="submit" id="submit" value="Annuler" name="submit" class="btn-default btn">Annuler</button>
    				</div>
    			{% endif %}
    			{% if action == 'suppression_entite' %}
    				<div class="form-group">
    					<button type="submit" id="submit" value="Supprimer" name="submit" class="btn-default btn">Supprimer</button>
    					<button type="submit" id="submit" value="Annuler" name="submit" class="btn-default btn">Annuler</button>
    				</div>
    			{% endif %}
    			{% if action == 'modification_entite' %}
    				<div class="form-group">
    					<button type="submit" id="submit" value="Modifier" name="submit" class="btn-default btn">Modifier</button>
    					<button type="submit" id="submit" value="Annuler" name="submit" class="btn-default btn">Annuler</button>
    				</div>
    			{% endif %}
    			{% if action == 'duplication_entite' %}
    				<div class="form-group">
    					<button type="submit" id="submit" value="Dupliquer" name="submit" class="btn-default btn">Dupliquer</button>
    					<button type="submit" id="submit" value="Annuler" name="submit" class="btn-default btn">Annuler</button>
    				</div>
    			{% endif %}
    		{{ form_end(form_entite) }}
    	</div>
    </div>
    {% if (action is empty) or (action == 'creation_entite') or (action == 'modification_entite') or (action == 'duplication_entite') %}
    	<script>
                    $( function() {
                            $( "#form_entite_date_debut" ).datepicker( $.datepicker.regional[ "fr" ] );
                            $( "#form_entite_date_fin" ).datepicker( $.datepicker.regional[ "fr" ] );
                    } );
            </script>
    {% endif %}


    Et dans mon contrôleur :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ->add('dateDebut', DateType::class, [
    	'label' => 'Date de début',
    	'widget' => 'single_text',
    	'html5' => false,
    	'attr' => ['id' => 'form_entite_date_debut', 'style' => 'width: 150px'],
    	'format' => 'dd/MM/yyyy',
    	'input' => 'datetime'
    ])
    ->add('dateFin', DateType::class, [
    	'label' => 'Date de fin',
    	'widget' => 'single_text',
    	'html5' => false,
    	'attr' => ['id' => 'form_entite_date_fin', 'style' => 'width: 150px'],
    	'format' => 'dd/MM/yyyy',
    	'input' => 'datetime'
    ])


    Mon code html généré :

    Code HTML :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    <div class="form-group"><label for="form_dateDebut" class="required">Date de début</label><input type="text" id="form_dateDebut" name="form[dateDebut]" required="required" id="form_entite_date_debut" style="width: 150px" class="form-control" /></div>
    <div class="form-group"><label for="form_dateFin" class="required">Date de fin</label><input type="text" id="form_dateFin" name="form[dateFin]" required="required" id="form_entite_date_fin" style="width: 150px" class="form-control" /></div>


    Je pense que le problème vient de l'id, qui est présent 2 fois pour chaque champ dans le input.
    id="form_dateDebut" et id="form_entite_date_debut"

    Et moi, je ne veux que l'id précisé dans mon contrôleur.
    J'ai raté une option quelque part ?
    Le problème vient de là, ou vous voyez autre chose ?

    Merci d'avance pour votre aide.

  6. #6
    Membre confirmé
    Pourquoi ne pas ajouter une classe "form_entite_datepicker" aux champs concernés et ajouter le datepicker via un sélecteur de classe plutôt qu'un sélecteur d'id ? (en remplaçant le # par un .)
    Code javascript :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    $( function() {
        $( ".form_entite_datepicker" ).datepicker( $.datepicker.regional[ "fr" ] );
    } );

  7. #7
    Membre averti
    Bonjour,

    J'ajoute mon petit grain de sel en indiquant la manière dont je travaille.

    Dans mon entité, mon attribut date est de type datetime, ce qui se répercute donc dans ma base de données également :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        /**
         * @ORM\Column(type="datetime")
         * 
         * @Assert\NotBlank(
         *      message = "The building date can't be empty"
         * )
         * @Assert\Type("\DateTime")
         * @Assert\LessThan("today")
         */
        private $manufactureDate;


    Voici le setter et les getters de cet attribut :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    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
        /**
         * Get the manufacture date
         *
         * @return \DateTimeInterface|null
         */
        public function getManufactureDate(): ?\DateTimeInterface
        {
     
            return $this->manufactureDate;
     
        }
     
        /**
         * Get a string of the manufacture date
         *
         * @return string
         */
        public function getFormattedManufactureDate(): string
        {
     
            return $this->manufactureDate->format('d-m-Y');
     
        }
     
        /**
         * Set the manufacture date
         *
         * @param \DateTimeInterface $manufactureDate
         * @return self
         */
        public function setManufactureDate(\DateTimeInterface $manufactureDate): self
        {
     
            $this->manufactureDate = $manufactureDate;
     
            return $this;
     
        }


    Au niveau de mon formulaire, voici la donnée qui est ajoutée :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
            $builder
                ->add('manufactureDate', DateType::class, array(
                                                                'widget' => 'single_text', 
                                                                'html5' => false, 
                                                                'attr' => ['class' => 'js-datepicker'],
                                                               )


    Ensuite, au niveau de la vue, je fais appel à du javascript (utilisation de moment.js) qui contient ceci pour l'utilisation du datepicker, du formatage des données après la saisie de la date et le reformatage des données au moment de l'envoi du formulaire :

    Code JavaScript :Sélectionner tout -Visualiser dans une fenêtre à part
    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
    //Using a calendar for the building vehicle date
    $(document).ready(function() {
     
        $(".js-datepicker").datepicker({
     
          viewMode: "years",
          weekStart: 1,
          daysOfWeekHighlighted: "6,0",
          autoclose: true,
          todayHighlight: true,
          startDate: '-30y',
          endDate:'+0d',
          format:  'yyyy-mm-dd'
     
        });
     
        $("#vehicle_manufactureDate").on('change', e => {
     
          $(this).val(moment($(this).val(), 'YYYY-MM-DD').format('L'));
     
        });
     
        $("form").submit(function(event) {
     
          var $mamufactureDateInput = $("#vehicle_manufactureDate");
          var sDate = $mamufactureDateInput.val();
          var dateTime = moment(sDate, localeDateFormat).toISOString();
     
          $mamufactureDateInput.val(dateTime);
     
        });
    ...


    De la sorte, je peux travailler sans souci avec le format de date de l'utilisateur, stocker et manipuler les dates de manière standard.

  8. #8
    Membre du Club
    @Thomas : Mais c'est pas bête ça !!!

    Donc dans mon template parent base.html.twig, j'ai mis :
    Code twig :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    <script>
            $( function() {
                    $( ".form_datepicker" ).datepicker( $.datepicker.regional[ "fr" ] );
            } );
    </script>


    Et dans tous mes formulaires où j'ai besoin d'un champ avec datepicker, je lui applique la classe form_datepicker.
    ça me plait !!!
    Merci beaucoup pour toute ton aide !

    @Dubitoph : Je ne connaissais pas moment.js.
    Je viens d'aller voir leur site, ça a l'air complet et puissant !
    Je garde cette possibilité en tête pour un futur site...

    Bon, sur ce, je mets mon sujet en résolu

###raw>template_hook.ano_emploi###