Symfony Forms Type Collection

Le problème exposé dans ce sujet a été résolu.

Salut :) !

Encore une question sur les formulaires de Symfony >_<

Trois Entity ici: Chart, ComparisonType, et la table de relation ChartComparisonType (que j’ai mis en entité car j’ai rajouté d’autres champs mais bref…) J’ai un formulaire pour l’entité Chart et je voudrais que l’utilisateur puisse ajouter des ComparisonType à son Chart par un select box multiple. Le select box affiche le nom de chaque ComparisonType déjà préalablement remplie dans la base de donnée (pas possible d’en ajouter quoi).

Ici un petit schéma pour comprendre les relations :

https://image.noelshack.com/fichiers/2018/31/5/1533282481-collection.png
https://image.noelshack.com/fichiers/2018/31/5/1533282481-collection.png

Donc je me suis dit, pas de souci, easy peasy, je commence par créer un collectionType dans le formulaire de mon entité Chart pour le champs "chartComparisonTypes" qui pointe vers un autre formulaire :

# The Chart Form
[...]
 ->add('chartComparisonTypes', CollectionType::class, [
      'entry_type'  => ChartComparisonTypeForm::class,
      'required'      => false,
 ])

# The ChartComparisonType Form
[...]
 ->add('comparisonType', EntityType::class, [
                'class'         => ComparisonType::class,
                'choice_label'  => function ($comparisonType) {
                    return $comparisonType->getName(); /* @var ComparisonType $comparisonType */
                },
                'required'      => false,
                'multiple'      => true,
                'expanded'      => false,
 ])

Seulement je n’arrive pas à mes fin, suis-je au moins sur la bonne voie ?

En vous remerciant ! :)

Salut !

La collection Chart#chartComparisonTypes contient bien des objets ChartComparisonType, mais ChartComparisonType#comparisonType ne peut contenir qu’un seul ComparisonType. En conséquence, le champ de formulaire pour ChartComparisonType#comparisonType ne peut pas avoir 'multiple' => true.

Une des manières que je verrais de permettre de choisir les ComparisonType avec des checkboxes impliquerait un champ non-mappé dans ChartForm pour lister les objets ComparisonType, et traitement supplémentaire dans le contrôleur pour créer les objets ChartComparisonType y relatifs — mais ça ne te permet évidemment pas de saisir les autres informations si ces dernières ne sont pas automatiques.
Dans ce dernier cas, tu pourrais imaginer enlever le champ pour ChartComparisonType#comparisonType dans ChartComparisonTypeForm, et au choix d’une des checkboxes du champ ci-dessus, ajouter un élément dans la collection Chart#chartComparisonType pour pouvoir saisir manuellement les informations nécessaires.

+0 -0

Salut Ymox !

Une des manières que je verrais de permettre de choisir les ComparisonType avec des checkboxes

Pour mon cas c’était plus un select box multiple dont j’avais besoin.

Deux formulaires

Mais du coup je comprends mieux pourquoi je galère depuis un moment… En faite à la limite un deuxième formulaire (donc deuxième createView) devrait faire l’affaire si côté controller je m’occupe de bien hydrater l’objet Chart comme il faut ?

Modifier mes relations

Ou alors je fais en sorte de mettre en place un ManyToMany simple entre Chart et ComparisonType, et les "extra fields" que j’avais besoin de rajouter dans mon "ChartComparisonType" je m’occupe de faire une table à part avec les bonnes liaisons (celles-ci ne seront jamais affiché dans un form)

Solution d’Ymox

Une des manières que je verrais de permettre de choisir les ComparisonType avec des checkboxes impliquerait un champ non-mappé dans ChartForm pour lister les objets ComparisonType, et traitement supplémentaire dans le contrôleur pour créer les objets ChartComparisonType y relatifs — mais ça ne te permet évidemment pas de saisir les autres informations si ces dernières ne sont pas automatiques.

Je peux essayer de faire ça effectivement étant donné que les autres informations n’ont pas besoin d’être saisi. Mais le côté checkbox me fait peur, je peux faire la même chose avec un selet box multiple non ?

Cases à cocher ou liste de choix à sélection multiple, ça ne change rien pour Symfony vu que ce sera un ArrayCollection qui te sera retourné dans les deux cas. C’est juste l’affichage qui change et, le cas échéant, le JavaScript qui doit s’occuper de ça.

+1 -0

Ça fonctionne, merci Ymox ;)

Mais en vrai je suis tellement frustré de devoir manuellement hydraté ce champs dans le Controller… Quand on sait qu’avec un simple ManyToMany, (sans extra fields a la table de relation), il n’y a pas de problème à faire ce que je veux :euh:

Je suis sûr qu’il doit y avoir un moyen mais je ne suis pas encore à l’aise avec les forms et la doc est pas si bien explicite que ça :(

En tout cas merci !

Il n’y a cependant pas de miracle. Si tu souhaites pouvoir choisir parmi une liste d’entités qui n’est pas liée directement à celle que tu manipules, tu vas devoir implémenter la gestion que tu souhaites en faire.

Après, tout est question de où la mettre. Certains la mettent dans l’entité manipulée (on aurait addComparisonType dans Chart, et dans cette méthode la création de l’objet ChartComparisonType), d’autres mettraient directement dans le contrôleur, d’autres créeraient un service appelé dans le contrôleur, d’autres créeraient un DataTransformer… Sans savoir ce que sont les autres champs de ChartComparisonType (et surtout si ces autres champs peuvent être automatisés — je pense aux dates de création et d’édition), c’est difficile de conseiller plus précisément.

+1 -0

on aurait addComparisonType dans Chart, et dans cette méthode la création de l’objet ChartComparisonType

Pour cette solution, addComparisonType est toujours appelé depuis le Controller c’est bien ça ?

Pour être un peu plus explicite sur les extra fields de ChartComparisonType : Il n’y en a pour l’instant qu’un seul : "performances", un champs relation de type One(ChartComparisonType )ToMany(Performance).

Un arrayCollection qui du coup reste vide quand on ajoute un ChartComparisonType à un Chart. C’est un champs un peu spécial qui est hydraté depuis un service à la volé juste avant l’affichage mais j’te passe un peu les détails.

Si c’est rempli à la volée lors de l’affichage, du coup il n’y a pas trop de souci si tu souhaitais creuser un peu plus la solution que je te proposais.

Après, je pense que le calcul de la performance est automatisable. Tu souhaites/dois conserver un historique, ou ça pourrait être recalculé à la volée ?

+1 -0

Yes Faut que je creuse un peu ta solution.

Je n’ai pas besoin de stocker l’historique des performances, c’est un calcul à la volé (qui tappe en gros sur des procédures stocké). J’hydrate la collection ChartComparisonType#performances pour chaque ChartComparisonType du Chart et hop j’affiche le tout sur le diagramme.

Quand je me relis, je me demande comment tu fais pour comprendre tout ça :lol:

Édit: qu’est ce que tu entends par "automatisable" ? Ce n’est pas déjà le cas dans la manière où je te le décris ?

+0 -0

Tu n’avais pas expressément précisé que c’était fait à la volée, et je pensais plus à un calcul côté PHP que côté base de données (ce qui est probablement plus rapide) — calcul qui se fait sur la durée intéressante, j’imagine, vu que la période du graphe est au choix de l’utilisateur. Mais j’avoue que je ne comprenais pas l’histoire de la propriété pour le résultat de ce calcul, d’autant que c’est en ManyToMany. Tu calcules des "sous-performances" ?

+1 -0

Tu calcules des "sous-performances" ?

L’utilisateur peux choisir le type de période (quotidien, mensuelle) sur un mois donné pour voir la performance de son indicateur. Un indicateur dans mon projet est un indice de performance. Par utilisateur, par jour, pleins d’informations sont stockées. A partir de ces informations brut, on créer des méthodes de calculs et hop, nous voilà avec un indicateur de performance. Après avec seulement un utilisateur et une date on peut avoir accès aux données.

Donc à la base, le diagramme, c’est juste pour afficher la performance de l’utilisateur de son indicateur sur une période donnée.

Donc rapidement, j’ai mis en place comme Entité : User ==> UserIndicator <== Indicator et sur l’entité de relation UserIndicator, j’ai rattaché une Entité Performance mais que j’ai nommé UserIndicatorPerformance.

Du coup je vais pouvoir répondre à ta question :p

Pour les "sous-performances" dont tu fais allusion et qui sont rattachés à l’entité ChartComparisonType, c’est pour stocker les performances des différents types de comparaisons comme la moyenne de l’équipe, les objectifs, bref pleins de trucs déjà gérer en base et où je n’ai plus qu’à "taper" sur les procédures stockées.

Donc j’ai nommé cette entité ChartComparisonTypePerformance. Ca peut paraître bizarre, mais j’ai pas trouvé mieux pour conceptualisé la base de données. Si j’avais gardé une seule entité Performance pour gérer tout ça, ça aurait compliqué tout.

Donc au final, l’utilisateur voit sur son diagramme la courbe de son indicateur, mais aussi les courbes des autres types de comparaison de ce même indicateur. (Un indicateur = un diagramme)

+0 -0

Hello,

Juste pour revenir dire que tout fonctionne à merveille (vacances entre temps, me jugez pas sur le temps que j’ai mis :D )

Merci encore @Ymox !

Ça m’a permit de mieux comprendre comment sa fonctionne sous le capot, comment Symfony hydrate avec les formulaires, etc…

Y’avait plusieurs solutions mais j’ai opté pour l’ajout d’un getter/adder/remover sur l’entité Chart d’un champs qui n’est, à la base, pas directement accessible depuis cette Entité. Je ne savais pas que c’était possible mais en faite on peut faire ce qu’on veut, c’est puissant ! :pirate:

<?php

# Entité Chart

[...]

    /**
     * @return Collection|ComparisonType[]
     */
    public function getComparisonTypes(): Collection
    {
        $comparisonTypes = new ArrayCollection();
        
        foreach ($this->getChartComparisonTypes() as $chartComparisonType) {
            $comparisonTypes->add($chartComparisonType->getComparisonType());
        }
        
        return $comparisonTypes;
    }
    
    /**
     * @param ComparisonType $comparisonType
     *
     * @return $this
     */
    public function addComparisonType(ComparisonType $comparisonType): self
    {
        $chartComparisonType = new ChartComparisonType($this, $comparisonType);
        $this->addChartComparisonType($chartComparisonType);
        
        return $this;
    }
    
    /**
     * @param ComparisonType $comparisonType
     *
     * @return $this
     */
    public function removeComparisonType(ComparisonType $comparisonType): self
    {
        foreach ($this->chartComparisonTypes as $chartComparisonType) {
            if ($chartComparisonType->getComparisonType() === $comparisonType) {
                $this->chartComparisonTypes->removeElement($chartComparisonType);
            }
        }
        
        return $this;
    }

[...]

Merci encore :)

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte