diff --git a/src/Controller/EpisodeController.php b/src/Controller/EpisodeController.php new file mode 100644 index 0000000..e4bca37 --- /dev/null +++ b/src/Controller/EpisodeController.php @@ -0,0 +1,151 @@ +render('episode/index.html.twig', [ + 'episodes' => $episodeRepository->findAll(), + ]); + } + + #[Route('/new', name: 'app_episode_new', methods: ['GET', 'POST'])] + public function new( + Request $request, + EntityManagerInterface $entityManager, + PodcastRepository $podcastRepository, + SluggerInterface $slugger, + ): Response + { + $queryPodcastId = $request->query->getInt('podcast', 0); + + $episode = new Episode(); + $form = $this->createForm( + EpisodeType::class, + $episode, + [ + 'owner' => $this->getUser(), + 'selectedPodcast' => $queryPodcastId, + ] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->logger->info("FLUMZO " . json_encode($form->getData())); + + // Check that the user is the owner of the podcast this episode should belong to. + $podcastId = $form->get('podcast')->getData(); + $podcast = $podcastRepository->find($podcastId ?? $queryPodcastId); + if ( + $podcast === null + || $podcast->getOwner()->getId() !== $this->getUser()->getId() + ) { + $form->get('podcast')->addError(new FormError('Invalid podcast.')); + return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); + } + $episode->setPodcast($podcast); + + // Ensure the uploaded audio file is saved properly. + if (!$this->handleAudioChange($form, $episode, $slugger)) { + $form->get('logo')->addError(new FormError('Could not upload audio.')); + return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); + } + + $entityManager->persist($episode); + $entityManager->flush(); + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('episode/new.html.twig', [ + 'episode' => $episode, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_episode_show', methods: ['GET'])] + public function show(Episode $episode): Response + { + return $this->render('episode/show.html.twig', [ + 'episode' => $episode, + ]); + } + + #[Route('/{id}/edit', name: 'app_episode_edit', methods: ['GET', 'POST'])] + public function edit(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response + { + $form = $this->createForm( + EpisodeType::class, + $episode, + ['selectedPodcast' => $episode->getPodcast()->getId()] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->flush(); + + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('episode/edit.html.twig', [ + 'episode' => $episode, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_episode_delete', methods: ['POST'])] + public function delete(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response + { + if ($this->isCsrfTokenValid('delete'.$episode->getId(), $request->request->get('_token'))) { + $entityManager->remove($episode); + $entityManager->flush(); + } + + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + protected function handleAudioChange( + FormInterface $form, + Episode $episode, + SluggerInterface $slugger + ): bool + { + $audioFile = $form->get('audio')->getData(); + if ($audioFile) { + $originalFilename = pathinfo($audioFile->getClientOriginalName(), PATHINFO_FILENAME); + $safeFilename = $slugger->slug($originalFilename); + $newFilename = $safeFilename . '-' . uniqid() . '.' . $audioFile->guessExtension(); + + try { + $audioFile->move($this->getParameter('audio_directory'), $newFilename); + } catch (FileException $e) { + return false; + } + + $episode->setAudioFilename($newFilename); + } + + return true; + } +} diff --git a/src/Controller/PodcastController.php b/src/Controller/PodcastController.php index 4032406..da401b6 100644 --- a/src/Controller/PodcastController.php +++ b/src/Controller/PodcastController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Entity\Podcast; use App\Form\PodcastType; +use App\Repository\EpisodeRepository; use App\Repository\PodcastRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -97,7 +98,11 @@ class PodcastController extends AbstractController } #[Route('/{id}', name: 'app_podcast_delete', methods: ['POST'])] - public function delete(Request $request, Podcast $podcast, EntityManagerInterface $entityManager): Response + public function delete( + Request $request, + Podcast $podcast, + EntityManagerInterface $entityManager + ): Response { if ($this->isCsrfTokenValid('delete'.$podcast->getId(), $request->request->get('_token'))) { $entityManager->remove($podcast); @@ -130,4 +135,16 @@ class PodcastController extends AbstractController return true; } + + #[Route('/{id}/episodes', name: 'app_podcast_episodes_index', methods: ['GET'])] + public function episodes_index( + Podcast $podcast, + EpisodeRepository $episodeRepository + ): Response + { + return $this->render('episode/index.html.twig', [ + 'podcast' => $podcast, + 'episodes' => $episodeRepository->findBy(['podcast' => $podcast]), + ]); + } } diff --git a/src/Form/EpisodeType.php b/src/Form/EpisodeType.php new file mode 100644 index 0000000..bdd23ed --- /dev/null +++ b/src/Form/EpisodeType.php @@ -0,0 +1,71 @@ +add('title') + ->add('description') + ->add('audio', FileType::class, [ + 'help' => 'The audio file of your episode.', + 'mapped' => false, + 'required' => false, + 'constraints' => new File([ + 'maxSize' => '4m', + 'mimeTypes' => ['audio/mpeg', 'audio/ogg', 'audio/opus', 'audio/aac', 'audio/flac', 'audio/webm'], + 'mimeTypesMessage' => 'Please select an audio file (MP3, OGG audio, Opus, AAC, FLAC, WebM audio).', + ]), + ]) + ->add('publicationDate') + ->add('podcast', EntityType::class, [ + 'help' => 'The podcast this episode belongs to. Cannot be changed after creation.', + 'class' => Podcast::class, + 'query_builder' => function (EntityRepository $er) use ($owner, $podcastId): QueryBuilder { + $qb = $er->createQueryBuilder('p'); + if ($owner !== null) { + $qb + ->andWhere('p.owner = :ownerId') + ->setParameter('ownerId', $owner->getId()); + } + if ($podcastId !== null) { + $qb + ->andWhere('p.id = :podcastId') + ->setParameter('podcastId', $podcastId); + } + return $qb->orderBy('p.name', 'ASC'); + + }, + 'placeholder' => false, + 'disabled' => $podcastId !== null, + 'empty_data' => "$podcastId", + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Episode::class, + 'owner' => null, + 'selectedPodcast' => null, + ]); + $resolver->setAllowedTypes('selectedPodcast', 'int'); + } +} diff --git a/templates/base.html.twig b/templates/base.html.twig index 7f9ff9b..efff7b6 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -13,6 +13,12 @@ {% endblock %}
+ {% if app.user %} + + {% endif %} {% block body %}{% endblock %}