[RESOLU] Symfony 4.3 Modal d'une vue dans une autre vue

Eléphanteau du PHP | 34 Messages

11 déc. 2019, 16:28

Bonjour,

Je vous explique mon problème, je suis en train de créer un site sur symfony 4.3 dont le but est de fournir des vidéos tutoriels à mes clients (fictifs). Je suis en train de construire le back-end et j'en suis au passage ou je veux ajouter des vidéos au niveau du back-end afin de les publier ensuite.

J'ai crée :

-un formulaire qui comprend les différents champs de saisies (titre, description, date etc...);

- un controller qui va récupérer les données du form pour la bdd

- une vue qui affiche le formulaire.

Je souhaite ajouter à cette vue la possibilité de rajouter la vidéo avec un bouton qui ouvre une modale.

Dans cette modale je veux ouvrir un dossier en particulier que j'ai nommé tmpfiles ou il n'y aura que les vidéos. De cette liste je veux sélectionner une vidéo valider mon form et rajouter la vidéo à une liste se trouvant en back-end.


En résumé on ajoute un titre, une date, une description, on clique sur un bouton du genre "choisir une vidéo", cela nous ouvre une modale et dans cette modale on y voit la liste des vidéos contenues dans un dossier se trouvant à la base du projet. On n'a pas un parcourir classic mais un accès à un dossier spécifique contenu dans le dossier du projet.

Ce que j'ai déjà fait :

Donc j'ai créée le chemin vers ce dossier dans mon fichier service.yaml =>

Code : Tout sélectionner

locale: 'en' uploads_directory: '%kernel.project_dir%/tmpfiles'
Dans mon controller j'ai créée deux fonctions, l'une se charge du formulaire (pour celle-ci aucun pb), l'autre utilise le component Finder (elle fonctionne aussi mais est-ce la bonne solution ?...)

Code : Tout sélectionner

class VideoController extends AbstractController { /** * @Route("/admin/video", name="video_video") * @param Request $request * @return RedirectResponse|Response */ public function register(Request $request) { $video = new Video(); $form = $this->createForm(VideoType::class, $video); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // On enregistre la video dans la base $em = $this->getDoctrine()->getManager(); $em->persist($video); $em->flush(); return $this->redirectToRoute('video_video'); } return $this->render( 'video/video.html.twig', array('form' => $form->createView()) ); } /** * @Route("/admin/video/modal", name="modal_video") * @param Request $request * @return RedirectResponse|Response */ public function finder(Request $request) { $uploadsDir = $this->getParameter('uploads_directory'); $finder = !is_dir($uploadsDir) ? [] : (new Finder())->files()->in($uploadsDir); return $this->render( 'video/modal.html.twig', array('finder' => $finder) ); } }
Ma vue ou je veux rajouter un bouton menant à la vue contenant le modal:

Code : Tout sélectionner

{% extends 'base.html.twig' %} {% block body %} <div class="container"> <div class="row"> <div class="col text-center"> <div class="col-xs-12"> <h1>Créer une vidéo</h1> {{ form_start(form) }} {{ form_row(form.titre, {'label': 'Titre : ', 'attr' :{'placeholder': 'titre ...' }}) }} {{ form_row(form.date) }} {{ form_row(form.descriptif, {'label': 'Description : ', 'attr' :{'placeholder': 'description ...' }}) }} {{ form_row(form.duree, {'label': 'Durée : ' }) }} {{ form_row(form.Projet, {'label': 'Projets : ' }) }} {{ form_row(form.Hashtag, {'label': 'Hashtags : ' }) }} {{ form_row(form.file) }} <!-- zone ou je veux mettre le bouton menant à la vue contenant le modal --> <!-- bouton pour valider le formulaire --> <button type="submit" class="btn btn-success">Enregister </button> {{ form_end(form) }} </div> </div> </div> </div> {% block javascripts %} <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" type="text/javascript"></script> {% endblock %} {% endblock %}<br><br>
Et ma vue contenant la modale :

Code : Tout sélectionner

<!-- The Modal --> <aside class="modal" data-target="#myModal"> <div class="modal-dialog"> <div class="modal-content"> <!-- Modal Header --> <div class="modal-header"> <h4 class="modal-title">Parcours des dossiers</h4> <button type="button" class="close" data-dismiss="modal">&times;</button> </div> <!-- Modal body --> <div class="modal-body"> <div class="row"> <div class="col"> <h2>Open file</h2> <hr> </div> </div> {# Contenu de la modal #} {% for file in finder %} <li> <a href="tmpfiles/{{ file.relativePathName }}"> {{ file.relativePathName }} </a> </li> {% endfor %} {# fin du contenu modal#} <div class="row" style="display: none" id="alert-box"> <div class="col"> <div class="alert alert-danger">File does not longer exists.</div> </div> </div> <div class="browser"></div> </div> <!-- Modal footer --> <div class="modal-footer"> <button type="button" class="btn btn-primary">Confirm</button> <button type="button" class="btn btn-danger" data-dismiss="modal">Close</button> </div> </div> </div> </aside> {% block javascripts %} <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" type="text/javascript"></script> {% endblock %}
Mon problème c'est que je ne veux pas que la modale s'ouvre sur une autre page mais sur la même ou se trouve le formulaire... mais si je mets la modale sur la même vue j'ai cette erreur " finder doesn't exist"
Du coup j'ai pensé à séparer la modale dans une vue et peut-être de l'ouvrir grâce à un bouton se trouvant dans l'autre vue...
Je suis débutante dans tout ça du coup ça fait des jours que je tourne en rond.... Je sais que je dois sans doute utiliser de l'ajax et du js, j'ai la compréhension mais pas l'application....
J'ai aussi testé l'include de twig et ça me rends la même erreur.
Si une âme charitable pouvait m'aider ça serait top.

Avatar du membre
Mammouth du PHP | 1019 Messages

11 déc. 2019, 17:07

Salut, c'est quoi le composant finder ? je vois que tu as un form row file, il est de quel type ? Pourquoi ne pas mettre par exemple une liste déroulante avec la liste des fichiers ?

Sinon effectivement, je pense que si tu veux faire une modale avec la liste des fichiers (grâce à ce composant finder), tu dois implémenter ça en ajax tout en créant une action qui va te permettre d'initialiser le composant finder que tu pourras ainsi utiliser dans ta vue.

PS : grosso modo tu ajoutes ton bouton qui fait un call ajax pour créer la vue de la liste et tu pousses le retour de cet appel dans la modale. Et dans la liste pour chaque fichier tu mets un bouton "Choisir ce fichier" qui lui, via javascript encore, va mettre à jour l'input file.
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 34 Messages

11 déc. 2019, 17:25

Salut,
merci d'avoir pris le temps d'étudier mon problème.
Le composant finder est un outil te permettant de chercher dans un fichier ou chercher un fichier ou même des dossiers ou dans un dossier.
Je le trouve approprié à ce que je veux faire ^^ (https://symfony.com/doc/current/components/finder.html).
Hum pour le row file, il est de type texte, j'ai pour objectif qu'une fois la vidéo sélectionnée, le nom (ou le chemin?) s'afficherait dans ce champ et une fois le formulaire validé, le chemin ou le nom serait enregistré en bdd.

Effectivement je n'ai pas pensé à la liste déroulante... c'est possible de récupérer des fichiers (ici des vidéos) provenant d'un dossier à la base du projet ? Parce que le nombre de vidéos présentes dans ce dossier sera sans doute variable.

Pour la solution ajax, je sens que ça va être plus compliqué et plus long (et je me suis fixée un temps limité...).

Avatar du membre
Mammouth du PHP | 1019 Messages

11 déc. 2019, 22:32

Alors oui tu peux dans ta classe de formulaire utiliser un ChoiceType et y injecter la liste des fichiers du répertoire via l'option choices. Tu pourrais pousser jusqu'à lui passer la liste des fichiers moins les fichiers déjà associés en travaillant un peu plus. Pour la lecture d'un répertoire tu peux basiquement utiliser les fonctions opendir et readdir mais si un composant Symfony plus élaboré fait le job, ça peut être très bien aussi.

https://symfony.com/doc/current/referen ... ml#choices
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 34 Messages

12 déc. 2019, 18:13

Salut,
J'ai tenté d'appliquer la solution "utiliser un ChoiceType et y injecter la liste des fichiers du répertoire via l'option choices", je bloque je ne vois pas ce que je dois rajouter dans l'array après choices. Dans les exemples de la doc ils mettent des valeurs définis mais ce n'est pas mon cas en soit non ?
Un peu plus de détail sur la méthode à appliquer ? :oops:

Avatar du membre
Mammouth du PHP | 1019 Messages

13 déc. 2019, 14:07

Serait-il possible de voir le code de ce que tu as tenté ?

A priori, si tu utilises le composant finder il faudrait que tu le passes en option à ta classe form.
https://symfony.com/doc/current/forms.h ... s-to-forms

Ainsi tu pourras récupérer la liste des fichiers directement dans la class de formulaire et construire le tableau des choices selon ton besoin.
The choices option is an array, where the array key is the item's label and the array value is the item's value

Ensuite si tu veux optimiser la liste déroulante (ne pas proposer les fichiers déjà associés) tu pourras ajouter une option pour passer doctrine au formulaire et récupérer la liste des fichiers déjà associés et les soustraire du tableau choices.

EDIT : en fait même pas besoin de passer le finder en option vu que ce n'est pas un service mais une class que tu instancies tout simplement. Donc tu as juste à faire le $finder = new Finder(); et construire le tableau choices.

class VideoType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      // ...
      ->add('file', ChoiceType::class, [
        'choices' => $this->getFiles(),
      ])
    ;
  }
  
  private function getFiles()
  {
    $finder = new Finder();
    $finder->files()->in('path/of/the/directory');
    
    $choices = [];
    
    foreach ($finder as $file)
      $choices[$file->getFilename()] = $file->getRealPath();
    
    return $choices;
  }

  // ...
}

Allez avec doctrine en plus pour filtrer la liste des fichiers, code non testé donc sujet à erreur et optimisable :
// src/Form/VideoType.php
class VideoType extends AbstractType
{
  public function buildForm(FormBuilderInterface $builder, array $options)
  {
    $builder
      // ...
      ->add('file', ChoiceType::class, [
        'choices' => $this->getFiles($options),
      ])
    ;
  }

  public function getFiles($options)
  {
    $linkedFiles = array_map(function ($video) {
      return $video->getFile();
    }, $options['doctrine']->getRepository(Video::class)->findAll());

    $finder = new Finder();
    $finder->files()->in('path/of/the/directory');

    $choices = [];

    foreach ($finder as $file)
      if (!in_array($file->getRealPath(), $linkedFiles))
        $choices[$file->getFilename()] = $file->getRealPath();

    return $choices;
  }

  public function configureOptions(OptionsResolver $resolver)
  {
    // ...
    $resolver->setRequired('doctrine');
  }

  // ...
}

// src/Controller/VideoController.php
class VideoController extends AbstractController
{
  public function new()
  {
    $form = $this->createForm(VideoType::class, new Video(), [
      'doctrine' => $this->getDoctrine(),
    ]);

    // ...
  }
}
J'édite souvent mon message après avoir répondu pour le corriger où y apporter des informations complémentaires alors n'hésitez pas à y jeter un nouveau coup d'oeil ^^

Eléphanteau du PHP | 34 Messages

13 déc. 2019, 16:32

Un grand merci Saian d'y avoir accordé de ton temps !
Ça marche impec' merci beaucoup ! :)