complete homemade, terrible, CRUD for entities

This commit is contained in:
dece 2024-02-25 20:19:03 +01:00
parent d5609f63d3
commit be579da23c
6 changed files with 92 additions and 79 deletions

View file

@ -2,6 +2,7 @@
namespace App\Controller; namespace App\Controller;
use App\Constants;
use App\Entity\Episode; use App\Entity\Episode;
use App\Form\EpisodeType; use App\Form\EpisodeType;
use App\Repository\EpisodeRepository; use App\Repository\EpisodeRepository;
@ -20,25 +21,25 @@ use Symfony\Component\String\Slugger\SluggerInterface;
#[Route('/manage/episodes')] #[Route('/manage/episodes')]
class EpisodeController extends AbstractController class EpisodeController extends AbstractController
{ {
public function __construct(protected LoggerInterface $logger) public function __construct(
{} protected EpisodeRepository $episodeRepo,
protected PodcastRepository $podcastRepo,
protected EntityManagerInterface $em,
protected LoggerInterface $logger,
protected SluggerInterface $slugger
) {
}
#[Route('/', name: 'app_episode_index', methods: ['GET'])] #[Route('/', name: 'app_episode_index', methods: ['GET'])]
public function index(EpisodeRepository $episodeRepository): Response public function index(): Response
{ {
return $this->render('episode/index.html.twig', [ return $this->render('episode/index.html.twig', [
'episodes' => $episodeRepository->findAll(), 'episodes' => $this->episodeRepo->findAll(),
]); ]);
} }
#[Route('/new', name: 'app_episode_new', methods: ['GET', 'POST'])] #[Route('/new', name: 'app_episode_new', methods: ['GET', 'POST'])]
public function new( public function new(Request $request): Response {
Request $request,
EntityManagerInterface $entityManager,
PodcastRepository $podcastRepository,
SluggerInterface $slugger,
): Response
{
$queryPodcastId = $request->query->getInt('podcast', 0); $queryPodcastId = $request->query->getInt('podcast', 0);
$episode = new Episode(); $episode = new Episode();
@ -53,28 +54,29 @@ class EpisodeController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { 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. // Check that the user is the owner of the podcast this episode should belong to.
$podcastId = $form->get('podcast')->getData(); $podcastId = $form->get('podcast')->getData();
$podcast = $podcastRepository->find($podcastId ?? $queryPodcastId); $podcast = $this->podcastRepo->find($podcastId ?? $queryPodcastId);
if ( if (
$podcast === null null === $podcast
|| $podcast->getOwner()->getId() !== $this->getUser()->getId() || $podcast->getOwner()?->getId() !== $this->getUser()?->getId()
) { ) {
$form->get('podcast')->addError(new FormError('Invalid podcast.')); $form->get('podcast')->addError(new FormError('Invalid podcast.'));
return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]);
} }
$episode->setPodcast($podcast); $episode->setPodcast($podcast);
// Ensure the uploaded audio file is saved properly. // Ensure the uploaded audio file is saved properly.
if (!$this->handleAudioChange($form, $episode, $slugger)) { if (!$this->handleAudioChange($form, $episode)) {
$form->get('logo')->addError(new FormError('Could not upload audio.')); $form->get('audio')->addError(new FormError('Could not upload audio.'));
return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]);
} }
$entityManager->persist($episode); $this->em->persist($episode);
$entityManager->flush(); $this->em->flush();
return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER);
} }
@ -89,11 +91,12 @@ class EpisodeController extends AbstractController
{ {
return $this->render('episode/show.html.twig', [ return $this->render('episode/show.html.twig', [
'episode' => $episode, 'episode' => $episode,
'files_path' => Constants::FILES_BASE_PATH
]); ]);
} }
#[Route('/{id}/edit', name: 'app_episode_edit', methods: ['GET', 'POST'])] #[Route('/{id}/edit', name: 'app_episode_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response public function edit(Episode $episode, Request $request): Response
{ {
$form = $this->createForm( $form = $this->createForm(
EpisodeType::class, EpisodeType::class,
@ -103,7 +106,17 @@ class EpisodeController extends AbstractController
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
$entityManager->flush(); $data = $form->getData();
$this->logger->notice(json_encode($data));
// Ensure the uploaded audio file is saved properly.
if (!$this->handleAudioChange($form, $episode)) {
$form->get('audio')->addError(new FormError('Could not upload audio.'));
return $this->render('episode/edit.html.twig', ['episode' => $episode, 'form' => $form]);
}
$this->em->persist($episode);
$this->em->flush();
return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER);
} }
@ -115,31 +128,32 @@ class EpisodeController extends AbstractController
} }
#[Route('/{id}', name: 'app_episode_delete', methods: ['POST'])] #[Route('/{id}', name: 'app_episode_delete', methods: ['POST'])]
public function delete(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response public function delete(Episode $episode, Request $request): Response
{ {
if ($this->isCsrfTokenValid('delete'.$episode->getId(), $request->request->get('_token'))) { if ($this->isCsrfTokenValid('delete'.$episode->getId(), strval($request->request->get('_token')))) {
$entityManager->remove($episode); $this->em->remove($episode);
$entityManager->flush(); $this->em->flush();
} }
return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER);
} }
protected function handleAudioChange( protected function handleAudioChange(FormInterface $form, Episode $episode): bool
FormInterface $form,
Episode $episode,
SluggerInterface $slugger
): bool
{ {
$audioFile = $form->get('audio')->getData(); $audioFile = $form->get('audio')->getData();
if ($audioFile) { if ($audioFile) {
$originalFilename = pathinfo($audioFile->getClientOriginalName(), PATHINFO_FILENAME); $originalFilename = pathinfo($audioFile->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = $slugger->slug($originalFilename); $safeFilename = $this->slugger->slug($originalFilename);
$newFilename = $safeFilename.'-'.uniqid().'.'.$audioFile->guessExtension(); $newFilename = $safeFilename.'-'.uniqid().'.'.$audioFile->guessExtension();
try { try {
$audioFile->move($this->getParameter('audio_directory'), $newFilename); $audioFile->move($this->getParameter('audio_directory'), $newFilename);
} catch (FileException $e) { } catch (FileException $exc) {
$this->logger->error(
'Failed to move audio file to audio directory: {msg}',
['msg' => $exc->getMessage()]
);
return false; return false;
} }

View file

@ -7,6 +7,7 @@ use App\Form\PodcastType;
use App\Repository\EpisodeRepository; use App\Repository\EpisodeRepository;
use App\Repository\PodcastRepository; use App\Repository\PodcastRepository;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
@ -19,28 +20,35 @@ use Symfony\Component\String\Slugger\SluggerInterface;
#[Route('/manage/podcasts')] #[Route('/manage/podcasts')]
class PodcastController extends AbstractController class PodcastController extends AbstractController
{ {
public function __construct(
protected PodcastRepository $podcastRepo,
protected EntityManagerInterface $em,
protected LoggerInterface $logger,
protected SluggerInterface $slugger
) {
}
#[Route('/', name: 'app_podcast_index', methods: ['GET'])] #[Route('/', name: 'app_podcast_index', methods: ['GET'])]
public function index(PodcastRepository $podcastRepository): Response public function index(): Response
{ {
$user = $this->getUser();
return $this->render('podcast/index.html.twig', [ return $this->render('podcast/index.html.twig', [
'podcasts' => $podcastRepository->findOwnedBy($this->getUser()) 'podcasts' => $user ? $this->podcastRepo->findOwnedBy($user) : [],
]); ]);
} }
#[Route('/new', name: 'app_podcast_new', methods: ['GET', 'POST'])] #[Route('/new', name: 'app_podcast_new', methods: ['GET', 'POST'])]
public function new( public function new(Request $request): Response
Request $request,
EntityManagerInterface $entityManager,
SluggerInterface $slugger,
): Response
{ {
$podcast = new Podcast(); $podcast = new Podcast();
$form = $this->createForm(PodcastType::class, $podcast); $form = $this->createForm(PodcastType::class, $podcast);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
if (!$this->handleLogoChange($form, $podcast, $slugger)) { if (!$this->handleLogoChange($form, $podcast)) {
$form->get('logo')->addError(new FormError('Could not upload logo.')); $form->get('logo')->addError(new FormError('Could not upload logo.'));
return $this->render('podcast/new.html.twig', [ return $this->render('podcast/new.html.twig', [
'podcast' => $podcast, 'podcast' => $podcast,
'form' => $form, 'form' => $form,
@ -52,8 +60,9 @@ class PodcastController extends AbstractController
} }
$podcast->setOwner($owner); $podcast->setOwner($owner);
$entityManager->persist($podcast); $this->em->persist($podcast);
$entityManager->flush(); $this->em->flush();
return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER);
} }
@ -72,21 +81,19 @@ class PodcastController extends AbstractController
} }
#[Route('/{id}/edit', name: 'app_podcast_edit', methods: ['GET', 'POST'])] #[Route('/{id}/edit', name: 'app_podcast_edit', methods: ['GET', 'POST'])]
public function edit( public function edit(Podcast $podcast, Request $request): Response
Request $request,
Podcast $podcast,
EntityManagerInterface $entityManager,
SluggerInterface $slugger,
): Response
{ {
$form = $this->createForm(PodcastType::class, $podcast); $form = $this->createForm(PodcastType::class, $podcast);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
if (!$this->handleLogoChange($form, $podcast, $slugger)) { $data = $form->getData();
$this->logger->notice(json_encode($data));
if (!$this->handleLogoChange($form, $podcast)) {
$form->get('logo')->addError(new FormError('Could not upload logo.')); $form->get('logo')->addError(new FormError('Could not upload logo.'));
} else { } else {
$entityManager->flush(); $this->em->flush();
return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER);
} }
} }
@ -98,30 +105,22 @@ class PodcastController extends AbstractController
} }
#[Route('/{id}', name: 'app_podcast_delete', methods: ['POST'])] #[Route('/{id}', name: 'app_podcast_delete', methods: ['POST'])]
public function delete( public function delete(Podcast $podcast, Request $request): Response
Request $request,
Podcast $podcast,
EntityManagerInterface $entityManager
): Response
{ {
if ($this->isCsrfTokenValid('delete'.$podcast->getId(), $request->request->get('_token'))) { if ($this->isCsrfTokenValid('delete'.$podcast->getId(), $request->request->get('_token'))) {
$entityManager->remove($podcast); $this->em->remove($podcast);
$entityManager->flush(); $this->em->flush();
} }
return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER); return $this->redirectToRoute('app_podcast_index', [], Response::HTTP_SEE_OTHER);
} }
protected function handleLogoChange( protected function handleLogoChange(FormInterface $form, Podcast $podcast): bool
FormInterface $form,
Podcast $podcast,
SluggerInterface $slugger
): bool
{ {
$logoFile = $form->get('logo')->getData(); $logoFile = $form->get('logo')->getData();
if ($logoFile) { if ($logoFile) {
$originalFilename = pathinfo($logoFile->getClientOriginalName(), PATHINFO_FILENAME); $originalFilename = pathinfo($logoFile->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = $slugger->slug($originalFilename); $safeFilename = $this->slugger->slug($originalFilename);
$newFilename = $safeFilename.'-'.uniqid().'.'.$logoFile->guessExtension(); $newFilename = $safeFilename.'-'.uniqid().'.'.$logoFile->guessExtension();
try { try {
@ -137,10 +136,7 @@ class PodcastController extends AbstractController
} }
#[Route('/{id}/episodes', name: 'app_podcast_episodes_index', methods: ['GET'])] #[Route('/{id}/episodes', name: 'app_podcast_episodes_index', methods: ['GET'])]
public function episodes_index( public function episodes_index(Podcast $podcast, EpisodeRepository $episodeRepository): Response
Podcast $podcast,
EpisodeRepository $episodeRepository
): Response
{ {
return $this->render('episode/index.html.twig', [ return $this->render('episode/index.html.twig', [
'podcast' => $podcast, 'podcast' => $podcast,

View file

@ -9,6 +9,7 @@ use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType; use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\File; use Symfony\Component\Validator\Constraints\File;
@ -28,7 +29,7 @@ class EpisodeType extends AbstractType
'mapped' => false, 'mapped' => false,
'required' => false, 'required' => false,
'constraints' => new File([ 'constraints' => new File([
'maxSize' => '4m', 'maxSize' => '512m',
'mimeTypes' => ['audio/mpeg', 'audio/ogg', 'audio/opus', 'audio/aac', 'audio/flac', 'audio/webm'], '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).', 'mimeTypesMessage' => 'Please select an audio file (MP3, OGG audio, Opus, AAC, FLAC, WebM audio).',
]), ]),
@ -54,8 +55,12 @@ class EpisodeType extends AbstractType
}, },
'placeholder' => false, 'placeholder' => false,
'disabled' => $podcastId !== null, 'disabled' => $podcastId !== null,
'empty_data' => "$podcastId", 'empty_data' => strval($podcastId),
]) ])
->add('submit', SubmitType::class, [
'label' => 'Save',
'attr' => ['class' => 'btn btn-primary']
]);
; ;
} }

View file

@ -3,7 +3,7 @@
namespace App\Repository; namespace App\Repository;
use App\Entity\Podcast; use App\Entity\Podcast;
use App\Entity\User; use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
@ -40,7 +40,8 @@ class PodcastRepository extends ServiceEntityRepository
} }
} }
public function findOwnedBy(User $user): array /** @return Podcast[] */
public function findOwnedBy(UserInterface $user): array
{ {
return $this->findBy(['owner' => $user], ['id' => 'ASC']); return $this->findBy(['owner' => $user], ['id' => 'ASC']);
} }

View file

@ -1,4 +1 @@
{{ form_start(form) }} {{ form(form) }}
{{ form_widget(form) }}
<button class="btn">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}

View file

@ -32,7 +32,7 @@
</tr> </tr>
<tr> <tr>
<th>Audio</th> <th>Audio</th>
<td><mark>TODO</mark></td> <td><audio controls src="{{ files_path }}{{ episode.audioFilename }}"></audio></td>
</tr> </tr>
<tr> <tr>
<th>Publication date</th> <th>Publication date</th>