15 Commits 21eb0058ac ... 2449f121f2

Author SHA1 Message Date
  Sangfroid 2449f121f2 Ajout de 5 films au hasard 2 months ago
  Sangfroid 1ae2a2d6d8 Couleur du bouton en cas de doublon 2 months ago
  Sangfroid dbf7aad511 Ajout de l'évitement des doublons dans recherche imdb 2 months ago
  Sangfroid ac684d3059 Eviter les listes de pagination trop longues 2 months ago
  Sangfroid c5cb42076f Suppression d'un dump 2 months ago
  Sangfroid fdaade9ad3 Filtres et pagination Ok 2 months ago
  Sangfroid b61405001b Ajout du nombre de résultats 2 months ago
  Sangfroid facb749546 Ajout du nombre de résultats 2 months ago
  Sangfroid 4bad005087 Pagination en avancement, seulement sur liste complète 2 months ago
  Sangfroid bbe5c490bd Correction si pas de filtre note 2 months ago
  Sangfroid 9507ffed63 Tous les filtres fonctionnels 2 months ago
  Sangfroid dde63e2768 Réorganisation controlleurs videotheque 2 months ago
  Sangfroid 57e5f44507 Un peu de factorisation des controlleurs 2 months ago
  Sangfroid a6d8554588 Filtres fonctionnels mais refactorisation à faire 2 months ago
  Sangfroid 5a312b68f0 Test DTO 2 months ago

+ 1 - 1
assets/app.js

@@ -21,5 +21,5 @@ const sv = switchView("#switchview");
 const se = switchEtat ('[data-fonction="switch"]');
 
 jQuery(function() {
-    $('[data-toggle="star-filter"]').filtreParNote();
+    //$('[data-toggle="star-filter"]').filtreParNote();
 })

+ 1 - 2
src/Controller/SearchController.php

@@ -18,8 +18,7 @@ class SearchController extends AbstractController
         $query = $request->query->get('q', "");
         return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
             'listeFilms'    =>  $filmSearch->search($query),
-            'titre'         =>  'Recherche '. $query,
-            'query'         =>  $query
+            'titre'         =>  'Recherche '. $query
         ));
     }
 }

+ 0 - 217
src/Controller/VideothequeController.php

@@ -1,217 +0,0 @@
-<?php
-
-namespace App\Controller;
-
-use App\Entity\Commentaire;
-use App\Entity\Realisateur;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\Routing\Annotation\Route;
-use Symfony\Component\HttpFoundation\Request;
-use App\Entity\Film;
-use App\Form\CommentaireType;
-use App\Form\FilmType;
-use App\Repository\CommentaireRepository;
-use App\Repository\FilmRepository;
-use App\Repository\GenreRepository;
-use App\Repository\RealisateurRepository;
-use App\Service\CommentaireManager;
-use App\Service\FilmManager;
-use App\Service\OptionsManager;
-use App\Service\TmdbApiService;
-use Symfony\Bundle\SecurityBundle\Security;
-use Symfony\Component\Form\FormFactoryInterface;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Security\Http\Attribute\IsGranted;
-use Symfony\Component\Serializer\SerializerInterface;
-
-class VideothequeController extends AbstractController
-{
-	#[Route("/", name: "videotheque_liste")]
-	public function listeAction(Request $request, FilmRepository $repo, OptionsManager $options): Response
-	{
-        $listeFilms = $repo->findTous();
-
-		return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
-            'listeFilms'	=>	$listeFilms,
-            'titre'         =>  'Liste complète',
-		));
-    }
-
-    #[Route("/prochaines-sorties", name: "prochaines_sorties")]
-    public function prochainesSorties(Request $request, FilmRepository $repo, OptionsManager $options): Response
-    {
-        $listeFilms = $repo->findProchaines();
-
-        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
-            'listeFilms'	=>	$listeFilms,
-            'titre'         =>  'Prochaines sorties et films en salle',
-		));
-    }
-
-    #[Route("/liste-by/{id}", name: "videotheque_listepargenre")]
-	public function  listeParGenreAction(\App\Entity\Genre $genre, FilmRepository $repo, OptionsManager $options): Response
-    {
-        $films = $repo->findFilmWithGenre(array($genre->getName()));
-
-        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
-            'listeFilms'    => $films,
-            'titre'         => 'Films par catégorie : '.$genre->getName()
-        ));
-    }
-
-    #[Route("/liste-by_real/{id}", name: "videotheque_listeparreal")]
-    public function  listeParRealisateurAction(Realisateur $realisateur, FilmRepository $repo, OptionsManager $options): Response
-    {
-        $films = $repo->findFilmWithReal(array($realisateur->getNomComplet()));
-
-        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
-            'listeFilms'    => $films,
-            'titre'         => 'Films par réalisateur : '.$realisateur->getNomComplet()
-        ));
-    }
-
-	#[Route("/ajouter/{idtmdb}", name: "videotheque_ajouter")]
-	public function ajouterAction(Request $request, FilmManager $filmManager, TmdbApiService $tmdbApiService, string $idtmdb=null): Response
-	{
-        if ($idtmdb !== null) {
-            $film = $tmdbApiService->hydrateFilm($idtmdb);
-        } else {
-    		$film = new Film;
-        }
-		$form = $this->createForm(FilmType::class, $film);
-
-		$form->handleRequest($request);
-		if ($form->isSubmitted() && $form->isValid())
-		{
-			$filmManager->addFilm($film);
-			$this->addFlash('success', 'Le film a été ajouté');
-			return $this->redirectToRoute('videotheque_voirfilm', array('id'=>$film->getId()));
-		}
-
-		return $this->render('videotheque/ajouter.html.twig', array(
-			'form'	=>	$form,
-		));
-	}
-
-    #[Route("/addtmdb", name: "videotheque_ajouter_tmdb")]
-    public function ajouterTmdb(Request $request, TmdbApiService $tmdbApiService): Response
-    {
-        $films = array();
-        $nbFilms = 0;
-        $nbPages = 0;
-        
-        $page = (int) $request->query->get('page', 1);
-        if ($page === 0) {
-            $page = 1;
-        }
-        $recherche = $request->query->get('query', "");
-
-        if ($tmdbApiService->query($recherche, $page)) {
-            $nbFilms = $tmdbApiService->countResults();
-            $nbPages = $tmdbApiService->countPages();
-            $films = $tmdbApiService->getFilms();
-        }
-        
-        return $this->render('videotheque/add_tmdb.html.twig', [
-            'titre' => 'Ajouter via TheMovieDB.org',
-            'films'  => $films,
-            'nbFilms'    => $nbFilms,
-            'nbPages'   => $nbPages,
-            'page'  => $page,
-            'recherche' => $recherche
-        ]);
-    }
-
-	#[Route("/modifier/{id}", name: "videotheque_modifier")]
-	public function modifierAction(Request $request, Film $film, FilmManager $filmManager): Response
-	{
-		$form = $this->createForm(FilmType::class, $film);
-		$form->handleRequest($request);
-		if ($form->isSubmitted() && $form->isValid())
-		{
-            $filmManager->editFilm($film);
-			$this->addFlash('success', 'Le film a été modifié');
-			return $this->redirectToRoute('videotheque_voirfilm',array('id'=>$film->getId()));
-		}
-
-		return $this->render('videotheque/modifier.html.twig', array(
-			'form'	=> $form,
-		));
-	}
-
-	#[Route("/supprimer/{id}", name: "videotheque_supprimer")]
-    #[IsGranted("ROLE_ADMIN")]
-	public function supprimerAction(Request $request, Film $film, FilmManager $filmManager, FormFactoryInterface $ffi): Response
-	{
-        $form = $ffi->create();
-        $form->handleRequest($request);
-        if ($form->isSubmitted() && $form->isValid())
-        {
-            $filmManager->delFilm($film);
-            $this->addFlash('success', 'Le film '.$film->getTitre().' a bien été supprimé.');
-            return $this->redirectToRoute('videotheque_liste');
-        }
-
-		return $this->render('videotheque/supprimer.html.twig', array(
-			'film'  =>  $film,
-            'form'  =>  $form
-		));
-	}
-
-	#[Route("/fichefilm/{id}", name: "videotheque_voirfilm")]
-	public function voirFilmAction(Request $request, \App\Entity\Film $film, CommentaireManager $cm, Security $security, CommentaireRepository $repo): Response
-    {
-        if ($security->isGranted('IS_AUTHENTICATED_REMEMBERED'))
-        {
-            $commentaireUser = $repo->findOneBy(array('film'=>$film, 'user'=>$this->getUser()));
-            if (is_null($commentaireUser))
-            {
-                $commentaireUser = new Commentaire();
-            }
-            $form = $this->createForm(CommentaireType::class, $commentaireUser);
-            $form->handleRequest($request);
-            if ($form->isSubmitted() && $form->isValid())
-            {
-                $cm->delEditAdd($commentaireUser, $film);
-                $this->addFlash('success', 'Le commentaire a été posté');
-                return $this->redirectToRoute('videotheque_voirfilm', array('id' => $film->getId()));
-            }
-        } else {
-            $form = $this->createForm(CommentaireType::class, null);
-        }
-        
-        return $this->render('videotheque/voirfilm.html.twig', array(
-            'film' => $film,
-            'form'  =>  $form
-        ));
-    }
-
-    #[Route("/ajax_req_realisateurs", name: "videotheque_ajax_realisateurs")]
-    public function ajaxRealisateurs(Request $request, RealisateurRepository $repo): Response
-    {
-        $query = $request->query->get('query');
-        $realisateurs = $repo->findRealisateurLike($query);
-        $liste = array();
-        foreach ($realisateurs as $realisateur)
-        {
-            $liste[] = $realisateur->getNomComplet();
-        }
-
-        return new JsonResponse($liste);
-    }
-
-    #[Route("/ajax_req_genres", name: "videotheque_ajax_genres")]
-    public function ajaxGenres(Request $request, GenreRepository $repo): Response
-    {
-        $query = $request->query->get('query');
-        $genres = $repo->findGenreLike($query);
-        $liste = array();
-        foreach ($genres as $genre)
-        {
-            $liste[] = $genre->getName();
-        }
-        
-        return new JsonResponse($liste);
-    }
-}

+ 141 - 0
src/Controller/VideothequeCrudController.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace App\Controller;
+
+use App\Entity\Film;
+use App\Form\FilmType;
+use App\Repository\FilmRepository;
+use App\Service\FilmManager;
+use App\Service\TmdbApiService;
+use Doctrine\ORM\EntityManagerInterface;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\Routing\Annotation\Route;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Security\Http\Attribute\IsGranted;
+
+class VideothequeCrudController extends AbstractController
+{
+    #[Route("/maliste/modifieravoir", name:"maliste_modifier_a_voir", methods: ['PATCH'])]
+    public function modifierFilmDansListeAction(Request $request, EntityManagerInterface $em, FilmRepository $repo, FilmManager $filmManager): Response
+    {
+        $result = null;
+        $film = $repo->find($request->toArray()['id_film']);
+        $result = $filmManager->inverseUserWantToView($film);
+        $em->flush();
+        /*$resultat = $this->get('serializer')->serialize($film, 'json');*/
+        return new JsonResponse((object)['newState' => $result]);
+    }
+
+    #[Route("/maliste/modifiervus", name:"maliste_modifier_vus", methods: ['PATCH'])]
+    public function modifierFilmVusAction(Request $request, FilmRepository $repo, EntityManagerInterface $em, FilmManager $filmManager): Response
+    {
+        $result = null;
+
+        $film = $repo->find($request->toArray()['id_film']);
+        $result = $filmManager->inverseUserWhoSeen($film);
+        $em->flush();
+        return new JsonResponse((object)['newState' => $result]);
+    }
+
+    #[Route("/ajouter/{idtmdb}", name: "videotheque_ajouter")]
+	public function ajouterAction(Request $request, FilmManager $filmManager, TmdbApiService $tmdbApiService, string $idtmdb=null): Response
+	{
+        if ($idtmdb !== null) {
+            $film = $tmdbApiService->hydrateFilm($idtmdb);
+        } else {
+    		$film = new Film;
+        }
+		$form = $this->createForm(FilmType::class, $film);
+
+		$form->handleRequest($request);
+		if ($form->isSubmitted() && $form->isValid())
+		{
+			$filmManager->addFilm($film);
+			$this->addFlash('success', 'Le film a été ajouté');
+			return $this->redirectToRoute('videotheque_voirfilm', array('id'=>$film->getId()));
+		}
+
+		return $this->render('videotheque/ajouter.html.twig', array(
+			'form'	=>	$form,
+		));
+	}
+
+    #[Route("/addtmdb", name: "videotheque_ajouter_tmdb")]
+    public function ajouterTmdb(
+        Request $request,
+        TmdbApiService $tmdbApiService,
+        FilmRepository $filmRepository
+    ): Response
+    {
+        $films = [];
+        $nbFilms = 0;
+        $nbPages = 0;
+        
+        $page = (int) $request->query->get('page', 1);
+        if ($page === 0) {
+            $page = 1;
+        }
+        $recherche = $request->query->get('query', "");
+
+        if ($tmdbApiService->query($recherche, $page)) {
+            $nbFilms = $tmdbApiService->countResults();
+            $nbPages = $tmdbApiService->countPages();
+            $films = $tmdbApiService->getFilms();
+            foreach($films as $key => $film) {
+                $filmDejaEnBase = $filmRepository->findFilmInImdb($film['data']);
+                foreach($filmDejaEnBase as $filmInBdd) {
+                    $films[$key]['filmBdd'][] = $filmInBdd;
+                }         
+            }
+        }
+        
+        return $this->render('videotheque/add_tmdb.html.twig', [
+            'titre' => 'Ajouter via TheMovieDB.org',
+            'films'  => $films,
+            'nbFilms'    => $nbFilms,
+            'nbPages'   => $nbPages,
+            'page'  => $page,
+            'recherche' => $recherche
+        ]);
+    }
+
+	#[Route("/modifier/{id}", name: "videotheque_modifier")]
+	public function modifierAction(Request $request, Film $film, FilmManager $filmManager): Response
+	{
+		$form = $this->createForm(FilmType::class, $film);
+		$form->handleRequest($request);
+		if ($form->isSubmitted() && $form->isValid())
+		{
+            $filmManager->editFilm($film);
+			$this->addFlash('success', 'Le film a été modifié');
+			return $this->redirectToRoute('videotheque_voirfilm',array('id'=>$film->getId()));
+		}
+
+		return $this->render('videotheque/modifier.html.twig', array(
+			'form'	=> $form,
+		));
+	}
+
+	#[Route("/supprimer/{id}", name: "videotheque_supprimer")]
+    #[IsGranted("ROLE_ADMIN")]
+	public function supprimerAction(Request $request, Film $film, FilmManager $filmManager, FormFactoryInterface $ffi): Response
+	{
+        $form = $ffi->create();
+        $form->handleRequest($request);
+        if ($form->isSubmitted() && $form->isValid())
+        {
+            $filmManager->delFilm($film);
+            $this->addFlash('success', 'Le film '.$film->getTitre().' a bien été supprimé.');
+            return $this->redirectToRoute('videotheque_liste');
+        }
+
+		return $this->render('videotheque/supprimer.html.twig', array(
+			'film'  =>  $film,
+            'form'  =>  $form
+		));
+	}
+
+}

+ 213 - 0
src/Controller/VideothequeListController.php

@@ -0,0 +1,213 @@
+<?php
+
+namespace App\Controller;
+
+use App\Dto\FiltersDTO;
+use App\Entity\Commentaire;
+use App\Entity\Realisateur;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\Routing\Annotation\Route;
+use Symfony\Component\HttpFoundation\Request;
+use App\Entity\Genre;
+use App\Form\CommentaireType;
+use App\Form\FiltersType;
+use App\Repository\CommentaireRepository;
+use App\Repository\FilmRepository;
+use App\Repository\GenreRepository;
+use App\Repository\RealisateurRepository;
+use App\Service\CommentaireManager;
+use App\Service\OptionsManager;
+use App\Service\Pagination;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Bundle\SecurityBundle\Security;
+use Symfony\Component\Form\FormFactoryInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Attribute\MapQueryString;
+
+class VideothequeListController extends AbstractController
+{
+	#[Route("/", name: "videotheque_liste")]
+	public function listeAction(
+        Request $request,
+        FilmRepository $filmRepository,
+        OptionsManager $options,
+        #[MapQueryString]
+        ?FiltersDTO $filters,
+        FormFactoryInterface $formFactory,
+    ): Response
+	{
+        $filters = $filters ?? new FiltersDTO();
+        $filtersForm = $formFactory->createNamed('', FiltersType::class, $filters);
+        
+        $filtersForm->handleRequest($request);
+
+        $qb = $filmRepository->queryTous($filtersForm->getData()->page, $filtersForm->getData()->toArray());
+        $pagination = new Pagination($qb, $filtersForm->getData()->limit, $filtersForm->getData()->page);
+
+		return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
+            'listeFilms'	=>	$pagination->getPaginator(),
+            'titre'         =>  'Liste complète',
+            'filtersForm'   =>  $filtersForm->createView(),
+            'pagination'    =>  $pagination
+		));
+    }
+
+    #[Route("/maliste", name:"videothequepersonnelle_maliste")]
+	public function maListeAction (
+        Request $request,
+        FilmRepository $filmRepository,
+        OptionsManager $options,
+        #[MapQueryString]
+        ?FiltersDTO $filters,
+        FormFactoryInterface $formFactory,
+    ): Response
+    {
+        $filters = $filters ?? new FiltersDTO();
+        $filters->userWantToView = $this->getUser();
+        $filtersForm = $formFactory->createNamed('', FiltersType::class, $filters);
+        
+        $filtersForm->handleRequest($request);
+
+        $qb = $filmRepository->queryTous($filtersForm->getData()->page, $filtersForm->getData()->toArray());
+        $pagination = new Pagination($qb, $filtersForm->getData()->limit, $filtersForm->getData()->page);
+
+		return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
+            'listeFilms'	=>	$pagination->getPaginator(),
+            'titre'         =>  'Ma liste de films à voir',
+            'filtersForm'   =>  $filtersForm->createView(),
+            'pagination'    =>  $pagination
+		));
+    }
+
+    #[Route("/prochaines-sorties", name: "prochaines_sorties")]
+    public function prochainesSorties(FilmRepository $repo, OptionsManager $options): Response
+    {
+        $listeFilms = $repo->findProchaines();
+
+        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
+            'listeFilms'	=>	$listeFilms,
+            'titre'         =>  'Prochaines sorties et films en salle',
+		));
+    }
+
+    #[Route("/liste-by/{id}", name: "videotheque_listepargenre")]
+	public function listeParGenreAction(
+        Request $request,
+        Genre $genre,
+        FilmRepository $filmRepository,
+        OptionsManager $options,
+        #[MapQueryString]
+        ?FiltersDTO $filters,
+        FormFactoryInterface $formFactory,
+    ): Response
+    {
+        $filters = $filters ?? new FiltersDTO();
+        $filtersForm = $formFactory->createNamed('', FiltersType::class, $filters);
+        
+        $filtersForm->handleRequest($request);
+
+        $qb = $filmRepository->queryFilmWithGenre([$genre->getName()], $filtersForm->getData()->page, $filtersForm->getData()->toArray());
+        $pagination = new Pagination($qb, $filtersForm->getData()->limit, $filtersForm->getData()->page);
+
+		return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
+            'listeFilms'	=>	$pagination->getPaginator(),
+            'titre'         =>  'Films par genre : ' . $genre->getName(),
+            'filtersForm'   =>  $filtersForm->createView(),
+            'pagination'    =>  $pagination
+		));
+    }
+
+    #[Route("/liste-by_real/{id}", name: "videotheque_listeparreal")]
+    public function  listeParRealisateurAction(
+        Request $request,
+        Realisateur $realisateur,
+        FilmRepository $filmRepository,
+        OptionsManager $options,
+        #[MapQueryString]
+        ?FiltersDTO $filters,
+        FormFactoryInterface $formFactory,): Response
+    {
+        $filters = $filters ?? new FiltersDTO();
+        $filtersForm = $formFactory->createNamed('', FiltersType::class, $filters);
+        
+        $filtersForm->handleRequest($request);
+
+        $qb = $filmRepository->queryFilmWithReal([$realisateur->getNomComplet()], $filtersForm->getData()->page, $filtersForm->getData()->toArray());
+        $pagination = new Pagination($qb, $filtersForm->getData()->limit, $filtersForm->getData()->page);
+
+		return $this->render('videotheque/liste_'.$options->vue().'.html.twig', [
+            'listeFilms'	=>	$pagination->getPaginator(),
+            'titre'         =>  'Films par réalisateur : ' . $realisateur->getNomComplet(),
+            'filtersForm'   =>  $filtersForm->createView(),
+            'pagination'    =>  $pagination
+		]);
+    }
+
+    #[Route('/pif', name: 'videotheque_random')]
+    public function randomAction(FilmRepository $filmRepository, OptionsManager $options): Response
+    {
+        $films = $filmRepository->findRandom(5);
+
+        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', [
+            'listeFilms'    => $films,
+            'titre'         => '5 films au hasard'
+        ]);
+    }
+
+	#[Route("/fichefilm/{id}", name: "videotheque_voirfilm")]
+	public function voirFilmAction(Request $request, \App\Entity\Film $film, CommentaireManager $cm, Security $security, CommentaireRepository $repo): Response
+    {
+        if ($security->isGranted('IS_AUTHENTICATED_REMEMBERED'))
+        {
+            $commentaireUser = $repo->findOneBy(array('film'=>$film, 'user'=>$this->getUser()));
+            if (is_null($commentaireUser))
+            {
+                $commentaireUser = new Commentaire();
+            }
+            $form = $this->createForm(CommentaireType::class, $commentaireUser);
+            $form->handleRequest($request);
+            if ($form->isSubmitted() && $form->isValid())
+            {
+                $cm->delEditAdd($commentaireUser, $film);
+                $this->addFlash('success', 'Le commentaire a été posté');
+                return $this->redirectToRoute('videotheque_voirfilm', array('id' => $film->getId()));
+            }
+        } else {
+            $form = $this->createForm(CommentaireType::class, null);
+        }
+        
+        return $this->render('videotheque/voirfilm.html.twig', array(
+            'film' => $film,
+            'form'  =>  $form
+        ));
+    }
+
+    #[Route("/ajax_req_realisateurs", name: "videotheque_ajax_realisateurs")]
+    public function ajaxRealisateurs(Request $request, RealisateurRepository $repo): Response
+    {
+        $query = $request->query->get('query');
+        $realisateurs = $repo->findRealisateurLike($query);
+        $liste = array();
+        foreach ($realisateurs as $realisateur)
+        {
+            $liste[] = $realisateur->getNomComplet();
+        }
+
+        return new JsonResponse($liste);
+    }
+
+    #[Route("/ajax_req_genres", name: "videotheque_ajax_genres")]
+    public function ajaxGenres(Request $request, GenreRepository $repo): Response
+    {
+        $query = $request->query->get('query');
+        $genres = $repo->findGenreLike($query);
+        $liste = array();
+        foreach ($genres as $genre)
+        {
+            $liste[] = $genre->getName();
+        }
+        
+        return new JsonResponse($liste);
+    }
+}

+ 0 - 50
src/Controller/VideothequePersonnelleController.php

@@ -1,50 +0,0 @@
-<?php
-
-namespace App\Controller;
-
-use App\Repository\FilmRepository;
-use App\Service\FilmManager;
-use App\Service\OptionsManager;
-use Doctrine\ORM\EntityManagerInterface;
-use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
-use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\Routing\Annotation\Route;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-
-class VideothequePersonnelleController extends AbstractController
-{
-	#[Route("/maliste", name:"videothequepersonnelle_maliste")]
-	public function maListeAction (Request $request, FilmRepository $repo, OptionsManager $options): Response
-    {
-        $films = $repo->findTousFavoritesByUser($this->getUser());
-
-        return $this->render('videotheque/liste_'.$options->vue().'.html.twig', array(
-            'listeFilms'    =>  $films,
-            'titre'         =>  'Ma liste de films à voir'
-        ));
-    }
-
-    #[Route("/maliste/modifieravoir", name:"maliste_modifier_a_voir", methods: ['PATCH'])]
-    public function modifierFilmDansListeAction(Request $request, EntityManagerInterface $em, FilmRepository $repo, FilmManager $filmManager): Response
-    {
-        $result = null;
-        $film = $repo->find($request->toArray()['id_film']);
-        $result = $filmManager->inverseUserWantToView($film);
-        $em->flush();
-        /*$resultat = $this->get('serializer')->serialize($film, 'json');*/
-        return new JsonResponse((object)['newState' => $result]);
-    }
-
-    #[Route("/maliste/modifiervus", name:"maliste_modifier_vus", methods: ['PATCH'])]
-    public function modifierFilmVusAction(Request $request, FilmRepository $repo, EntityManagerInterface $em, FilmManager $filmManager): Response
-    {
-        $result = null;
-
-        $film = $repo->find($request->toArray()['id_film']);
-        $result = $filmManager->inverseUserWhoSeen($film);
-        $em->flush();
-        return new JsonResponse((object)['newState' => $result]);
-    }
-
-}

+ 46 - 0
src/Dto/FiltersDTO.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Dto;
+
+use App\Entity\User;
+use Symfony\Component\Validator\Constraints as Assert;
+
+class FiltersDTO
+{
+    public function __construct(
+        #[Assert\NotBlank()]
+        #[Assert\GreaterThanOrEqual(10)]
+        #[Assert\LessThanOrEqual(100)]
+        public int $limit = 25,
+
+        public ?User $userName = null,
+        
+        public null|int|string $note = 0,
+        
+        #[Assert\NotBlank()]
+        public string $sortBy = 'dateSubmited',
+
+        #[Assert\Choice(['ASC', 'DESC'])]
+        public string $sortOrder = 'DESC',
+
+        public ?User $userWantToView = null,
+
+        public int $page = 1
+    )
+    {
+        $this->note = $this->note === '' ? null : (int)$this->note;
+    }
+
+    public function toArray(): array
+    {
+        return [
+            'limit'     => $this->limit,
+            'username'  => $this->userName,
+            'sortBy'    => $this->sortBy,
+            'sortOrder' => $this->sortOrder,
+            'note'      => (int)$this->note,
+            'userWantToView' => $this->userWantToView,
+            'page'      => $this->page
+        ];
+    }
+}

+ 82 - 0
src/Form/FiltersType.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Form;
+
+use App\Dto\FiltersDTO;
+use App\Entity\User;
+use App\Repository\UserRepository;
+use Doctrine\ORM\QueryBuilder;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+use Symfony\Component\Form\Extension\Core\Type\NumberType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class FiltersType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options): void
+    {
+        $builder
+            ->add('note', NumberType::class, [
+                'required'  => false,
+                'label'       => "Note au dessus de",
+                'attr'      => [
+                    'data-clear-value' => '0',
+                    'class' => "rating",
+                    'data-min-threshold' => '0',
+                    'data-min'  => '0',
+                    'data-max'  => '5',
+                    'data-step' => 1,
+                    'data-show-clear'   => "true",
+                    'data-show-caption' => "false",
+                    'data-size'         => "sm",
+                    'data-theme'          => "krajee-fa"
+                ],
+            ])
+            ->add('limit', ChoiceType::class, [
+                'choices' => [
+                    '10' => 10,
+                    '25' => 25,
+                    '50' => 50,
+                    '100' => 100
+                ]
+            ])
+            ->add('userName', EntityType::class, [
+                'required' => false,
+                'class' => User::class,
+                'choice_label' => 'username',
+                'choice_value' => function(?User $user): string {
+                        return $user ? $user->getUsername() : '';
+                },
+                'query_builder' => function (UserRepository $er): QueryBuilder {
+                    return $er->createQueryBuilder('u')
+                        ->orderBy('u.username', 'ASC');
+                }
+            ])
+            ->add('sortBy', ChoiceType::class, [
+                'choices' => [
+                    'Date de soumission' => 'dateSubmited',
+                    'Titre' => 'titre',
+                    'Année' => 'annee',
+                    'Note' => 'note'
+                ]
+            ])
+            ->add('sortOrder', ChoiceType::class, [
+                'choices' => [
+                    'ASC' => 'ASC',
+                    'DESC' => 'DESC'
+                ]
+            ])
+        ;
+    }
+
+    public function configureOptions(OptionsResolver $resolver): void
+    {
+        $resolver->setDefaults([
+            'data_class'    => FiltersDTO::class,
+            'method'        => 'GET',
+            'csrf_protection' => false,
+        ]);
+    }
+}

+ 104 - 42
src/Repository/FilmRepository.php

@@ -4,6 +4,7 @@ namespace App\Repository;
 
 use App\Entity\Film;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\ORM\QueryBuilder;
 use Doctrine\Persistence\ManagerRegistry;
 
 /**
@@ -31,31 +32,47 @@ class FilmRepository extends ServiceEntityRepository
         return $query->getSingleResult();
     }
 
-    public function findFilmWithGenre(array $genreNames) : array
+    public function queryFilmWithGenre(array $genreNames, int $page = 1, array $filters = []) : QueryBuilder
     {
-        $qb = $this->createQueryBuilder('f');
+        $qb = $this->queryFilter($page, $filters);
         $qb
             ->innerJoin('f.genres', 'g')
             ->addSelect('f');
 
-        $qb->where($qb->expr()->in('g.name', $genreNames));
+        $qb
+            ->andWhere($qb->expr()->in('g.name', ':genrenames'))
+            ->setParameter('genrenames', $genreNames)
+        ;
 
-        return $qb
+        return $qb;
+    }
+
+    public function findFilmWithGenre(array $genreNames): array
+    {
+        return $this->queryFilmWithGenre($genreNames)
             ->getQuery()
-            ->getResult();
+            ->getResult()
+        ;
     }
 
-    public function findFilmWithReal(array $realisateursnames): array
+    public function queryFilmWithReal(array $realisateursNames, int $page = 1, array $filters = []): QueryBuilder
     {
-        $qb = $this->createQueryBuilder('f');
+        $qb = $this->queryFilter($page, $filters);
         $qb
             ->leftJoin('f.realisateurs', 'rea')
             ->addSelect('f')
-            ->where($qb->expr()->in('rea.nomComplet', $realisateursnames));
+            ->andWhere($qb->expr()->in('rea.nomComplet', ':realisateurs'))
+            ->setParameter('realisateurs', $realisateursNames)
+        ;
+        return $qb;
+    }
 
-        return $qb
+    public function findFilmWithReal(array $realisateursNames): array
+    {
+        return $this->queryFilmWithReal($realisateursNames)
             ->getQuery()
-            ->getResult();
+            ->getResult()
+        ;
     }
 
     public function findFilmWithRealLike($query): array
@@ -87,46 +104,51 @@ class FilmRepository extends ServiceEntityRepository
             ->getResult();
     }
 
-
-
-    public function findTousDesc(): array
+    public function queryFilter(int $page = 1, array $filters = []): QueryBuilder
     {
-        $qb = $this->createQueryBuilder('f');
-        $qb
-            ->select('f')
-            ->orderBy('f.dateSubmited', 'DESC');
-
-        return $qb
-            ->getQuery()
-            ->getResult();
-    }
+        $qb =  $this->createQueryBuilder('f')
+            ->orderBy('f.'.($filters['sortBy'] ?? 'dateSubmited'), $filters['sortOrder'] ?? 'DESC')
+        ;
 
-    public function findTous (): array
-    {
-        $qb = $this->createQueryBuilder('f')
-            ->leftJoin('f.authered', 'aut')->addSelect('aut')
-            ->leftJoin('f.genres', 'gen')->addSelect('gen')
-            ->leftJoin('f.realisateurs', 'rea')->addSelect('rea')
-            ->orderBy('f.dateSubmited', 'DESC');
-        return $qb
-            ->getQuery()
-            ->getResult();
+        if (!empty($filters['userWantToView'])) {
+            $qb
+                ->leftJoin('f.usersWantToView', 'wan')
+                ->andWhere($qb->expr()->in('wan', ':user'))
+                ->setParameter('user', $filters['userWantToView'])
+            ;
+        }
+
+        if (!empty($filters['limit'])) {
+            $limit = (int)$filters['limit'];
+            $qb->setFirstResult(($page * $limit) - $limit);
+            $qb->setMaxResults($limit);
+        }
+    
+        if (!empty($filters['note'])) {
+            $qb
+                ->andWhere('f.note >= :note')
+                ->setParameter('note', $filters['note'])
+            ;
+        }    
+        
+        if (!empty($filters['username'])) {
+            $qb
+                ->andWhere('f.authered = :authered')
+                ->setParameter('authered', $filters['username'])
+            ;
+        }
+        return $qb;
+        
     }
 
-    public function findTousFavoritesByUser($user): array
+    public function queryTous ($page = 1, array $filters = []): QueryBuilder
     {
-        $qb = $this->createQueryBuilder('f');
-        $qb
+        $qb = $this->queryFilter($page, $filters)
             ->leftJoin('f.authered', 'aut')->addSelect('aut')
             ->leftJoin('f.genres', 'gen')->addSelect('gen')
             ->leftJoin('f.realisateurs', 'rea')->addSelect('rea')
-            ->leftJoin('f.usersWantToView', 'wan')
-            ->where($qb->expr()->in('wan', ':user'))
-            ->setParameter('user', $user)
-            ->orderBy('f.dateSubmited', 'DESC');
-        return $qb
-            ->getQuery()
-            ->getResult();
+        ;
+        return $qb;
     }
 
     public function findProchaines(): array
@@ -158,4 +180,44 @@ class FilmRepository extends ServiceEntityRepository
             ->getResult()
         ;
     }
+
+    public function findFilmInImdb(Film $film): array
+    {
+        $reals = [];
+        foreach($film->getRealisateurs() as $real) {
+            $reals[] = $real->getNomComplet();
+        }
+        $qb = $this->createQueryBuilder('f');
+        $qb
+            ->andWhere('f.titre = :titre')
+            ->setParameter('titre', $film->getTitre())
+            ->innerJoin('f.realisateurs', 'rea')
+            ->andWhere($qb->expr()->in('rea.nomComplet', ':reals'))
+            ->setParameter('reals', $reals)
+        ;
+        return $qb
+            ->getQuery()
+            ->getResult()
+        ;
+    }
+
+    public function findRandom(int $nb = 5): array
+    {
+        $films = $this->createQueryBuilder('f')
+            ->select('f.id')
+            ->getQuery()
+            ->getResult()
+        ;
+        $ids = array_flip(array_map(fn($value): int => $value['id'], $films));
+
+        $random = array_rand($ids, $nb);
+
+        $qb = $this->createQueryBuilder('f');
+        $qb->andWhere($qb->expr()->in('f.id', $random));
+        
+        return $qb
+            ->getQuery()
+            ->getResult()
+        ;
+    }
 }

+ 42 - 0
src/Service/Pagination.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Service;
+
+use Doctrine\ORM\QueryBuilder;
+use Doctrine\ORM\Tools\Pagination\Paginator;
+
+class Pagination
+{
+    protected readonly Paginator $paginator;
+
+    public function __construct(
+        protected readonly QueryBuilder $queryBuilder,
+        protected readonly int $limit,
+        protected readonly int $currentPage
+    )
+    {
+        $this->paginator = new Paginator($this->queryBuilder);
+    }
+
+    public function getPaginator(): Paginator
+    {
+        return $this->paginator;
+    }
+
+    public function getTotal(): int
+    {
+        return $this->paginator->count();
+    }
+
+    public function getPages(): int
+    {
+        $pages = ceil($this->paginator->count() / $this->limit);
+        $pages = $pages > 0 ? $pages: 1;
+        return $pages;
+    }
+
+    public function getCurrentPage(): int
+    {
+        return $this->currentPage;
+    }
+}

+ 1 - 1
src/Service/TmdbApiService.php

@@ -90,7 +90,7 @@ class TmdbApiService
         foreach($entrees as $entree) {
             $film = $this->hydrateFilm($entree->id);
 
-            $this->films[$entree->id] = $film;
+            $this->films[$entree->id]['data'] = $film;
         }
     }
 

+ 12 - 6
templates/navbar.html.twig

@@ -22,13 +22,19 @@
                 {% if app.request.attributes.get('_route') == 'prochaines_sorties' %}active{% endif %}"
                 href="{{ path('prochaines_sorties') }}">Prochaines sorties</a>
             </li>
-            {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
-            <li class="nav-item">
-                <a class="nav-link
-                {% if app.request.attributes.get('_route') == 'realisateur_liste' %}active{% endif %}"
-                href="{{ path('realisateur_liste') }}">Réalisateurs</a>
+            <li class="nav-item dropdown">
+                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownActions" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                    Actions
+                </a>
+                <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
+                    <li><a class="dropdown-item" href="{{ path('videotheque_random') }}">5 films au hasard</a></li>
+                    {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
+                    <li class="nav-item">
+                        <a class="dropdown-item" href="{{ path('realisateur_liste') }}">Réalisateurs</a>
+                    </li>
+                    {% endif %}
+                </ul>
             </li>
-            {% endif %}
             {%  if is_granted('ROLE_MODERATEUR') %}
             <li class="nav-item dropdown">
                 <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownAdmin" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

+ 20 - 0
templates/videotheque/_filters_form.html.twig

@@ -0,0 +1,20 @@
+{% if filtersForm is defined %}
+<div data-controller="collapser mb-3">
+    <button class="btn text-primary" data-bs-toggle="collapse" data-bs-target="#collapse-filters">Filtres <i data-collapser-target="button" class="fa fa-chevron-circle-right fa-lg"></i></button>
+    <div id="collapse-filters" class="collapse" data-collapser-target="collapse">
+        {#{% form_theme filtersForm 'bootstrap_5_horizontal_layout.html.twig' %}#}
+            {{ form_start(filtersForm, {'attr': {'class': 'row row-cols-lg-auto g-3 align-items-center'} } ) }}
+        
+            {{ form_row(filtersForm.note) }}
+            {{ form_row(filtersForm.limit) }}
+            {{ form_row(filtersForm.userName) }}
+
+            {{ form_row(filtersForm.sortBy) }}
+            {{ form_row(filtersForm.sortOrder) }}
+            {{ form_rest(filtersForm)}}
+            
+            <button class="btn btn-primary mb-3" type="submit">Filtrer</button>
+            {{ form_end(filtersForm) }}
+    </div>
+</div>
+{% endif %}

+ 58 - 0
templates/videotheque/_liste_footer.html.twig

@@ -0,0 +1,58 @@
+{% if pagination is defined %}
+<nav aria-label="pagination">
+    {% set param = app.request.attributes.get('_route_params')['id'] ?? '' %}
+    {% set pages = pagination.getPages() %}
+    {% set currentPage = pagination.getCurrentPage() %}
+    <ul class="pagination">
+        {# <li class="page-item">
+            <a
+                class="page-link"
+                href="{{ path(app.request.attributes.get('_route'), app.request.query|merge({'id': param, 'page': 1})) }}"
+            >
+                {{ "First" | trans }}
+            </a>
+        </li> #}
+        <li class="page-item {{ currentPage > 1 ?: 'disabled' }}">
+            <a
+                class="page-link"
+                aria-label="Previous"
+                href="{{ path(app.request.attributes.get('_route'), app.request.query|merge({'id': param, 'page': currentPage - 1})) }}"
+            >
+                <span aria-hidden="true">&laquo;</span>
+            </a>
+        </li>
+        {% for i in 1..pages %}
+        {% if (currentPage - i < 3  and currentPage - i > -3) or (i == 1) or (i == pages) %}
+        <li class="page-item {{ currentPage is not same as i ?: 'active'}}">
+            <a
+                class="page-link"
+                href="{{ path(app.request.attributes.get('_route'), app.request.query|merge({'id': param, 'page': i} )) }}"
+            >
+                {{ i }}
+            </a>
+        </li>
+        {% elseif currentPage - i == 3 or currentPage - i == -3 %}
+            <li class="page-item mx-2">...</li> 
+        {% endif %}
+        {% endfor %}
+        <li class="page-item {{ currentPage < pages ?: 'disabled' }}">
+            <a
+                aria-label="Next"
+                class="page-link"
+                href="{{ path(app.request.attributes.get('_route'), app.request.query|merge({'id': param, 'page': currentPage + 1})) }}"
+            >
+                <span aria-hidden="true">&raquo;</span>
+            </a>
+        </li>
+        {# <li class="page-item">
+            <a
+                class="page-link"
+                href="{{ path(app.request.attributes.get('_route'), app.request.query|merge({'id': param, 'page': pages})) }}"
+            >
+                {{ "Last" | trans }}
+            </a>
+        </li> #}
+    </ul>
+</nav>
+<div class="mb-6"></div>
+{% endif %}

+ 7 - 18
templates/videotheque/_liste_header.html.twig

@@ -2,22 +2,11 @@
     {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
         <p class="me-2"><a class="btn btn-primary" role="button" href="{{ path('videotheque_ajouter') }}"><i class="fa fa-plus-square fa-lg"></i> Ajouter un film</a></p>
         <p class="me-2"><a class="btn btn-primary" role="button" href="{{ path('videotheque_ajouter_tmdb') }}"><i class="fa fa-plus-square fa-lg"></i> Ajouter par TMDB</a></p>
-        <p class="me-auto"><a href="#" role="button" class="btn btn-secondary" id="switchview">Changer de vue</a></p>
+        <p class="me-2"><a href="#" role="button" class="btn btn-secondary" id="switchview">Changer de vue</a></p>
           
-        {% endif %}
-    <p class="">Filtrer par film au dessus de
-    <input class="rating"
-        data-disabled="false"
-        data-show-clear="true"
-        data-show-caption="false"
-        data-theme="krajee-fa"
-        data-toggle="star-filter"
-        data-path="#tableFilms"
-        style="display:none;"
-        min=0
-        max=5
-        data-step=1
-        data-size="sm"
-        value="0">
-    </p>
-</div>
+    {% endif %}
+</div>
+{{ include ('videotheque/_filters_form.html.twig') }}
+
+
+<h6 class="mb-3">{{ listeFilms | length }} Résultats</h6>

+ 24 - 8
templates/videotheque/add_tmdb.html.twig

@@ -53,14 +53,30 @@
                 <tr>
             {% endif %}
             {% for idtmdb, film in films %}
-                <tr class="{{ film.information ? '' : 'border-bottom' }}">
-                    <td><a type="button" class="btn btn-primary" href="{{ path('videotheque_ajouter', {'idtmdb': idtmdb }) }}">Créér</a></td>
-                    <td>{{ film.titre }}</td>
-                    <td>{% if film.dateSortie %}{{ film.dateSortie | date ('d/m/Y')}}{% endif %}</td>
-                    <td>{% for genre in film.genres %}<span class="badge bg-info me-1">{{ genre.name }}</span>{% endfor %}</td>
-                    <td>{% for realisateur in film.realisateurs %}<span class="badge bg-info me-1">{{ realisateur.nomComplet }}</span>{% endfor %}</td>
+                {% if film['filmBdd'] is defined %}
+                <tr>
+                    <td colspan="5">
+                        <div class="alert alert-warning mb-0" role="alert">
+                            Ce film est peut-être déjà en base : 
+                            {% for filmBdd in film['filmBdd'] %}
+                                &nbsp;<a href="{{ path('videotheque_voirfilm', {'id': filmBdd.id} ) }}">Aller sur la fiche</a>
+                            {% endfor %}
+                        </div>
+                    </td>
+                </tr>
+                {% endif %}
+                <tr class="{{ film['data'].information ? '' : 'border-bottom' }}">
+                    <td>
+                        <a type="button" class="btn btn-{{ film['filmBdd'] is defined ? 'warning' : 'primary' }}" href="{{ path('videotheque_ajouter', {'idtmdb': idtmdb }) }}">
+                            Créér{{ film['filmBdd'] is defined ? ' quand même' : '' }}
+                        </a>
+                    </td>
+                    <td>{{ film['data'].titre }}</td>
+                    <td>{% if film['data'].dateSortie %}{{ film['data'].dateSortie | date ('d/m/Y')}}{% endif %}</td>
+                    <td>{% for genre in film['data'].genres %}<span class="badge bg-info me-1">{{ genre.name }}</span>{% endfor %}</td>
+                    <td>{% for realisateur in film['data'].realisateurs %}<span class="badge bg-info me-1">{{ realisateur.nomComplet }}</span>{% endfor %}</td>
                 </tr>
-                {% if film.information %}
+                {% if film['data'].information %}
                     <tr data-controller="collapser" class="border-bottom">
                         <td colspan="5">
                             <div class="d-flex">
@@ -70,7 +86,7 @@
                                 </a>
                             </div>
                             <div id="collapseResume-{{ idtmdb }}" class="collapse" data-collapser-target="collapse">
-                                {{ film.information }}
+                                {{ film['data'].information }}
                             </div>
                         </td>
                     </tr>

+ 2 - 1
templates/videotheque/liste_tableaux.html.twig

@@ -111,7 +111,7 @@
 							value="{{ film.note }}">
 					{% endif %}
 				</td>
-				<td>{{ film.annee | date('Y') }}</td>
+				<td>{% if film.annee is not null %}{{ film.annee | date('Y') }}{% endif %}</td>
 				<td>
 					{% if film.dateSortie %}{{ film.dateSortie | date('d/m/y')}}{% endif %}
 				</td>
@@ -119,4 +119,5 @@
 			{% endfor %}
 		</tbody>
 	</table>
+	{{ include('videotheque/_liste_footer.html.twig') }}
 {% endblock %}

+ 8 - 3
templates/videotheque/liste_vignettes.html.twig

@@ -17,9 +17,11 @@
 		{% for film in listeFilms %}
 		<article class="card border-primary mb-4" data-controller="collapser" data-auteur="{{ film.authered.nomComplet }}">
 			<div class="card-header d-flex justify-content-between">
-				<h3 class="card-title"><a class="" href="{{ path('videotheque_voirfilm', {'id': film.id}) }}"><span data-bs-toggle="tooltip" data-placement="right" title="{{ film.information }}">{{ film.titre }}</span></a>
-					
-				</h3>
+				<h4 class="card-title">
+					<a class="" href="{{ path('videotheque_voirfilm', {'id': film.id}) }}">
+						<span data-bs-toggle="tooltip" data-placement="right" title="{{ film.information }}">{{ film.titre }}</span>
+					</a>	
+				</h4>
 				<div class="d-flex">
 					{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
 						<div class="me-3">
@@ -67,10 +69,12 @@
 			<div class="card-body collapse" id="collapse-id-{{ film.id }}" data-collapser-target="collapse">
 				<table class="table">
 					<tbody>
+						{% if film.annee %}
 						<tr>
 							<th style="width: 9em;">Année</th>
 							<td>{{ film.annee | date('Y') }}</td>
 						<tr>
+						{% endif %}
 						<tr>
 							<th>Réalisateur(s)</th>
 							<td>
@@ -143,4 +147,5 @@
 		</article>
 		{% endfor %}
 	</section>
+	{{ include('videotheque/_liste_footer.html.twig') }}
 {% endblock %}

+ 2 - 0
templates/videotheque/voirfilm.html.twig

@@ -81,8 +81,10 @@
                 </div>
                 <div class="card-body">
                     <dl class="row">
+                        {% if film.annee %}
                         <dt class="col-4">Année</dt>
                         <dd class="col-8">{{ film.annee | date('Y') }}</dd>
+                        {% endif %}
                         <dt class="col-4">Réalisateur(s)</dt>
                         <dd class="col-8">
                         {% for realisateur in film.realisateurs %}

+ 13 - 1
translations/messages.fr.yaml

@@ -23,6 +23,8 @@ Content: Contenu de la page
 edit_page: Editer la page
 Previous: Précédente
 Next: Suivante
+First: "<<"
+Last: ">>"
 
 Username: Nom d'utilisateur
 Prenom: Prénom
@@ -41,4 +43,14 @@ Super Admin: Super Admin
 Compte activé: Compte activé
 Activated: Compte activé
 Oui: Oui
-Non: Non
+Non: Non
+
+Note au dessus de: Note au dessus de 
+Nb results: Résultats par page
+User name: Films ajoutés par
+Sort by: Tri par
+Sort order: Ordre
+ASC: Croissant
+DESC: Décroissant
+Date de soumission: Date de soumission
+Note: Note