THELIA Forum

Welcome to the THELIA support and discusssion forum

Announcement

Rejoignez la communauté sur le Discord Thelia : https://discord.gg/YgwpYEE3y3

Offline


Bonjour,

Pour un module custom qui reprend le fonctionnement du carousel en plus poussé, j'ai besoin de pouvoir gérer des dates.

Mon schema.xml ressemble à ça :

<table name="background">
        <column autoIncrement="true" name="id" primaryKey="true" required="true" type="INTEGER" />
        <column name="file" type="VARCHAR" size="255" />
        <column name="position" type="INTEGER" />
        <column name="alt" size="255" type="VARCHAR" />
        <column name="url" size="255" type="VARCHAR" />
        <column name="title" size="255" type="VARCHAR" />
        <column name="description" type="CLOB" />
        <column name="chapo" type="LONGVARCHAR" />
        <column name="postscriptum" type="LONGVARCHAR" />
        <column name="type" size="15" type="VARCHAR" required="true" defaultValue="carousel" />
        <column name="start" type="DATE" required="false" />
        <column name="end" type="DATE" required="false" />
        <column name="active" type="BOOLEAN" required="true" defaultValue="false" />
        <column name="lang" size="15" type="VARCHAR" required="true" defaultValue="fr_FR" />
        <behavior name="timestampable" />
        <behavior name="i18n">
            <parameter name="i18n_columns" value="alt, title, description, chapo, postscriptum" />
        </behavior>
</table>

Ce schéma me donne cette table SQL :
Capture.png

La déclaration de mon form :

protected function buildForm()
    {
        $formBuilder = $this->formBuilder;

        $backgrounds = BackgroundQuery::create()->orderByPosition()->find();

        /** @var Background $background */
        foreach ($backgrounds as $background) {
            $id = $background->getId();

            $formBuilder->add(

... blabla ...

            )->add('start'.$id, 'date', array(
                'label' => Translator::getInstance()->trans('Date de début'),
                'label_attr' => array(
                    'for' => 'start'.$id
                ),
                'required' => false
            ))->add('end'.$id, 'date', array(
                'label' => Translator::getInstance()->trans('Date de fin'),
                'label_attr' => array(
                    'for' => 'end'.$id
                ),
                'required' => false
            ));
        }
    }

Mon template Back Office :

</div>
    {render_form_field field="start{$ID}" value=$START}
    {render_form_field field="end{$ID}" value=$END}
<div>

Et la tête qu'il a, avec le problème qui survient quand j'essaie de lui donner une date via le datepicker qui se met automatiquement :
Capture2.png

C'est lorsque j'essaie d'enregistrer cette modif que j'obtiens cette erreur en rouge sur l'image. Je pense que le format de la date ne lui va pas, mais je ne trouve ni réponse dans le forum, ni exemple dans la doc sur une utilisation d'un form avec une date... Un petit coup de pouce serait le bienvenu. smile

Last edited by HeishPi (17-06-2019 09:36:31)


Développeur web Junior

Offline


J'ai tenté de changer mon schema.xml pour que mes champs start et end deviennent des varchar, en me disant que je n'aurais qu'à lui filer des dates parsées en string (dans le bon format, bien sûr). Pour bien faire, j'ai arrêté d'utiliser les méthodes du buildForm pour ces deux inputs, et j'ai fait des inputs normaux :

<div class="col-sm-6">
    {custom_render_form_field field="start{$ID}"}
        <label for="start{$ID}">Date de début</label>
        <input id="start{$ID}" class="form-control" type="date" value="{$START}" />
     {/custom_render_form_field}
</div>
<div class="input-group col-sm-6">
    {custom_render_form_field field="end{$ID}"}
        <label for="end{$ID}">Date de fin</label>
        <input id="end{$ID}" class="form-control" type="date" value="{$END}" />
    {/custom_render_form_field}
</div>

Malheureusement, je n'arrive même pas jusqu'au reformatage. Je pense que la validation du formulaire empêche le traitement correct des dates. Je n'obtiens que des dates invalides, qui sont automatiquement remplacées par la date initial du timestamp : 01/01/1970 !

J'ai essayé un tas de configuration différente mais je n'arrive pas à lui passer des données valides. Une idée qui me permettrait au moins de garder le datepicker de Thelia dans le formulaire ?


Développeur web Junior

Offline


Si tu veux pouvoir saisir un champ de type date, il te faut utiliser 3 champs, un pour l'année, un pour le mois, un pour le jour. Par exemple :

    {form_field field="start$ID"}
        {$year = $smarty.now|date_format:'%Y'}
        <label for="start{$ID}">Date de début</label>
            
        <select name="{$name}['year']">
            {for $idx = $year to $year+10}
                <option value="{$idx}"{if $idx == $value['year']} selected{/if}>{$idx}</option>
            {/for}
        </select>
            
        <select name="{$name}['month']">
            {for $idx = 1 to 12}
                <option value="{$idx}"{if $idx == $value['month']} selected{/if}>{$idx}</option>
            {/for}
        </select>
            
        <select name="{$name}['day']">
            {for $idx = 1 to 31}
                <option value="{$idx}"{if $idx == $value['day']} selected{/if}>{$idx}</option>
            {/for}
        </select>
    {/form_field}    

Je n'ai pas testé ce code, mais globalement, ça doit être proche.

Dans ton contrôleur, tu récupère un objet DateTime.


OpenStudio Toulouse

Offline


Et ben, fallait le savoir...
Bon du coup j'ai adapté ton code à ma sauce :

{form_field field="start$ID"}                                 
{$year = $smarty.now|date_format:'%Y'}                  
<label for="start{$ID}">Date de début</label> 
        
<select name="{$name}['day']">   
    <option value="" disabled>Jour</option>
    {for $idx = 1 to 31}
        <option value="{$idx}" {if $idx == $value['day']} selected{/if}>{$idx}</option>
    {/for}
</select>

                   etc...

{/form_field}

J'ai bien sûr remis mes deux champs en DATE dans la BDD (j'avais changé pour VARCHAR histoire de tenter autre chose), j'ai remis le bon modèle, le form aussi réclame à nouveau des objets "date".

Le formulaire s'affiche bien mais je ne récupère pas la value de chaque entrée, aucune "option" n'est en "selected". Et je ne vois pas trop pourquoi, $value['day'] ça devrait marcher, non ?





EDIT : Problème résolu en faisant ça :

{form_field field="start$ID"}
{$year = $smarty.now|date_format:'%Y'}
<label for="start{$ID}">Date de début</label>
{assign var="startDay" $START|date_format:'d'}
{assign var="startMonth" $START|date_format:'m'}
{assign var="startYear" $START|date_format:'Y'}

<select name="{$name}['day']">
	<option value="" disabled>Jour</option>
	{for $idx = 1 to 31}
		<option value="{$idx}" {if $idx == $startDay} selected{/if}>{$idx}</option>
	{/for}
</select>

<select name="{$name}['month']">
	<option value="" disabled>Mois</option>
	{for $idx = 1 to 12}
		<option value="{$idx}" {if $idx == $startMonth} selected{/if}>{$idx}</option>
	{/for}
</select>

<select name="{$name}['year']">
	<option value="" disabled>Année</option>
	{for $idx = $year to $year+5}
		<option value="{$idx}" {if $idx == $startYear} selected{/if}>{$idx}</option>
	{/for}
</select>
{/form_field}

Last edited by HeishPi (13-06-2019 10:55:28)


Développeur web Junior

Offline


En revanche, impossible de valider le formulaire avec ce format là. Le contrôleur attends effectivement un objet datetime mais je lui envoie ça à la place :

background_update[start40]['day']: 20
background_update[start40]['month']: 7
background_update[start40]['year']: 2019

Aurais-tu une idée de la manière de reformater ça en datetime AVANT la validation du formulaire ?

PS : Le contrôleur adapté depuis le module carousel :

public function updateAction()
    {
        if (null !== $response = $this->checkAuth(AdminResources::MODULE, ['background'], AccessManager::UPDATE)) {
            return $response;
        }

        $form = $this->createForm('background.update');

        $error_message = null;

        try {
            $updateForm = $this->validateForm($form);

            $backgrounds = BackgroundQuery::create()->find();

            $locale = $this->getCurrentEditionLocale();

            /** @var Background $background */
            foreach ($backgrounds as $background) {
                if($background->getLang() == $locale){
                    $id = $background->getId();

                    $background
                        ->setPosition($this->getFormFieldValue($updateForm, 'position', $id))
                        ->setUrl($this->getFormFieldValue($updateForm, 'url', $id))
                        ->setLocale($locale)
                        ->setTitle($this->getFormFieldValue($updateForm, 'title', $id))
                        ->setAlt($this->getFormFieldValue($updateForm, 'alt', $id))
                        ->setChapo($this->getFormFieldValue($updateForm, 'chapo', $id))
                        ->setDescription($this->getFormFieldValue($updateForm, 'description', $id))
                        ->setPostscriptum($this->getFormFieldValue($updateForm, 'postscriptum', $id))
                        ->setType($this->getFormFieldValue($updateForm, 'type', $id))
                        ->setActive($this->getFormFieldValue($updateForm, 'active', $id))
                        ->setLang($this->getFormFieldValue($updateForm, 'lang', $id))
                        ->setStart($this->getFormFieldValue($updateForm, 'start', $id))
                        ->setEnd($this->getFormFieldValue($updateForm, 'end', $id))
                        ->save();
                }
            }

            $response =  $this->redirectToConfigurationPage();

        } catch (FormValidationException $e) {
            $error_message = $this->createStandardFormValidationErrorMessage($e);
        }

        if (null !== $error_message) {
            $this->setupFormErrorContext(
                'background upload',
                $error_message,
                $form
            );

            $response = $this->render("module-configure", [ 'module_code' => 'Background' ]);
        }

        return $response;

    }

Last edited by HeishPi (13-06-2019 15:05:34)


Développeur web Junior

Offline


Et ben, fallait le savoir...

Un peu comme tout, hein...

Le contrôleur n'attend rien du tout, c'est la form qui attend quelque chose.

En fait, je me demande si ce n'est pas years, months, et days qu'il faut utiliser au lieu de year month day


OpenStudio Toulouse

Offline


Effectivement, avec Thelia y a peu de place à l'imagination, et il rarement plusieurs bonnes manières de faire. Faut juste savoir comment, à chaque fois. Heureusement que le forum est là, d'ailleurs.

Pareil en ajoutant les "s", ça n'a pas l'air de passer la validation du formulaire. Est-ce que faudrait écrire ce dernier différemment ?

Mon form :

->add('start'.$id, 'date', array(
	'label' => Translator::getInstance()->trans('Date de début'),
	'label_attr' => array(
        	'for' => 'start'.$id
	),
	'required' => false
))->add('end'.$id, 'date', array(
	'label' => Translator::getInstance()->trans('Date de fin'),
	'label_attr' => array(
		'for' => 'end'.$id
	),
	'required' => false
));

Développeur web Junior

Offline


Non, non, j'ai trouvé le problème, c'est les simples quotes dans les noms des champs. Il faut utiliser {$name}[day] et non pas {$name}['day'].

Cette version est correctement validée (enfin, si tu n'essaie pas d'entrer le 30 février ou une autre date invalide) :

       
{form_field field="date"}
    <label>Date de début</label>

    {$startDay = $smarty.now|date_format:'d'}
    {$startMonth = $smarty.now|date_format:'m'}
    {$startYear = $smarty.now|date_format:'Y'}

    {$theYear = $smarty.now|date_format:'Y'}

    <select name="{$name}[day]">
        <option value="" disabled>Jour</option>
        {for $idx = 1 to 31}
            <option value="{$idx}" {if $idx == $value['day']|default:$startDay} selected{/if}>{$idx}</option>
        {/for}
    </select>

    <select name="{$name}[month]">
        <option value="" disabled>Mois</option>
        {for $idx = 1 to 12}
            <option value="{$idx}" {if $idx == $value['month']|default:$startMonth} selected{/if}>{$idx}</option>
        {/for}
    </select>

    <select name="{$name}[year]">
        <option value="" disabled>Année</option>
        {for $idx = $theYear to $theYear+5}
            <option value="{$idx}" {if $idx == $value['year']|default:$startYear} selected{/if}>{$idx}</option>
        {/for}
    </select>
{/form_field}                    

Ton contrôleur va bien recevoir un objet de type DateTime, à toi de le gérer correctement.

De même, si tu popule ta form, tu dois passer une objet de type DateTime à la form.


OpenStudio Toulouse

Offline


Effectivement, ça marche ! Merci beaucoup de ton aide, je peux enfin avancer sur ce module ! D'ailleurs, à terme j'essaierai de le mettre à disposition. On sait jamais, si quelqu'un possède un site avec un carousel et des images de fond dont le changement doit être programmé par date...

Merci encore Roadster, t'es top ! cool


Développeur web Junior

Offline


Bonjour,

Je suis tombé également sur cette erreur mais je n'arrivais pas à me resoudre à construire les champs de formulaires à la main (qui aime faire ce genre de chose à la main ?).
Sachant que Thelia utilises est basé sur Symfony (même si la version actuellement supportée est la 2.8), j'ai creusé un peu dans les paramètre du composant Forms de Symfony et en configurant le champ date de mon formulaire comme indiqué ci-desous :

    protected function buildForm()
    {

        $this->formBuilder
            ->add("excluded_date", DateType::class, [
                'widget' => 'single_text',
                'input' => 'datetime',
                'label' => Translator::getInstance()->trans("Date d'exclusion", [], MoulinBooking::DOMAIN_NAME),
                'label_attr' => [
                    'for' => 'excluded_date',
                    'help' => $this->translator->trans('Date pour laquelle le retrait en boutique n\'est pas possible (jj/mm/aaaa)', [], MoulinBooking::DOMAIN_NAME)
                ]
            ])
            ->add("memo", TextType::class, [
                'required' => false,
                'label' => Translator::getInstance()->trans("NOte d'information", [], MoulinBooking::DOMAIN_NAME),
                'label_attr' => [
                    'for' => 'memo',
                    'help' => $this->translator->trans('Memo', [], MoulinBooking::DOMAIN_NAME)
                ]
            ])
        ;

    }

Je peux utiliser le tag Smarty {render_form_field} de Thelia pour créé automatiquement un champ input de saisie de date au html5

{render_form_field form=$form field='excluded_date'}

Dans le controller du module on récupère bien un objet PHP DateTime.

Tout n'était qu'une affaire de configuration finalement, notez les paramètres 'widget' => 'single_text' et 'input' => 'datetime' dans le form builder.

La vie est plus simple avec Thelia et Symfony cool (j'y ai quand même laissé quelques cheveux)