feat: rename admin screen, allow deleting a resource
This commit is contained in:
1 parent
dc1822b8cf
commit
5e5bf00f8f
7 files changed
+197
-9
No files matched your search
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin\Resource;
|
||||
|
||||
use App\Controller\Traits\ConfirmedActionTrait;
|
||||
use App\Entity\Booking;
|
||||
use App\Entity\BookingSeries;
|
||||
use App\Entity\Resource;
|
||||
use App\Htmx\HxRedirectResponse;
|
||||
use App\Repository\BookingRepository;
|
||||
use App\Security\Role;
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
/**
|
||||
* Deleting a resource for good, together with every booking and series on it.
|
||||
*
|
||||
* Retiring (EditController) is the way to keep the history; this is for a resource that is gone
|
||||
* or was created by mistake. The confirmation names how many bookings go with it.
|
||||
*
|
||||
* The foreign keys are ON DELETE RESTRICT, so the bookings are removed explicitly first. The
|
||||
* resource row is locked like BookingManager does, so a booking made while the modal is open
|
||||
* either lands before the lock -- and is deleted with the rest -- or waits and then fails.
|
||||
*/
|
||||
#[IsGranted(Role::ADMIN)]
|
||||
final class DeleteController extends AbstractController
|
||||
{
|
||||
use ConfirmedActionTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly BookingRepository $bookings,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/admin/resources/{id}/delete', name: 'app_admin_resource_delete', methods: ['GET', 'POST'])]
|
||||
public function index(Request $request, Resource $resource): Response
|
||||
{
|
||||
$tokenId = 'delete-resource-'.$resource->getId();
|
||||
|
||||
if (!$request->isMethod(Request::METHOD_POST)) {
|
||||
$total = $this->bookings->count(['resource' => $resource]);
|
||||
$upcoming = $this->bookings->countFromDate($resource, new \DateTimeImmutable('today'));
|
||||
|
||||
return $this->render('admin/_modal_resource_delete.html.twig', [
|
||||
'resource' => $resource,
|
||||
'upcoming' => $upcoming,
|
||||
'past' => $total - $upcoming,
|
||||
'csrf_token_id' => $tokenId,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->denyUnlessCsrfValid($request, $tokenId);
|
||||
|
||||
$id = $resource->getId();
|
||||
$label = $resource->getLabel();
|
||||
|
||||
$deleted = $this->em->wrapInTransaction(function () use ($resource): int {
|
||||
$this->em->lock($resource, LockMode::PESSIMISTIC_WRITE);
|
||||
|
||||
$deleted = $this->em->createQueryBuilder()
|
||||
->delete(Booking::class, 'b')
|
||||
->where('b.resource = :resource')
|
||||
->setParameter('resource', $resource)
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
$this->em->createQueryBuilder()
|
||||
->delete(BookingSeries::class, 's')
|
||||
->where('s.resource = :resource')
|
||||
->setParameter('resource', $resource)
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
$this->em->remove($resource);
|
||||
$this->em->flush();
|
||||
|
||||
return (int) $deleted;
|
||||
});
|
||||
|
||||
$this->logger->info('Admin deleted a resource.', [
|
||||
'admin' => $this->getUser()?->getUserIdentifier(),
|
||||
'resource_id' => $id,
|
||||
'label' => $label,
|
||||
'bookings_deleted' => $deleted,
|
||||
]);
|
||||
|
||||
$this->addFlash('success', $deleted > 0
|
||||
? \sprintf('%s und %d Buchungen gelöscht.', $label, $deleted)
|
||||
: \sprintf('%s gelöscht.', $label));
|
||||
|
||||
return new HxRedirectResponse($this->generateUrl('app_admin_resource'));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user