feat: draft inspection cli command
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Repository\BookingEditDraftRepository;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:draft:inspect',
|
||||
description: 'Inspect booking edit drafts in the database',
|
||||
)]
|
||||
class DraftInspectCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly BookingEditDraftRepository $draftRepository,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption('days', 'd', InputOption::VALUE_OPTIONAL, 'Show only drafts older than N days', null)
|
||||
->addOption('booking-id', 'b', InputOption::VALUE_OPTIONAL, 'Filter by booking ID', null)
|
||||
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete the listed drafts (use with caution)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$days = $input->getOption('days');
|
||||
$bookingId = $input->getOption('booking-id');
|
||||
$shouldDelete = $input->getOption('delete');
|
||||
|
||||
$qb = $this->draftRepository->createQueryBuilder('d')
|
||||
->join('d.user', 'u')
|
||||
->select('d', 'u')
|
||||
->orderBy('d.updatedAt', 'DESC');
|
||||
|
||||
if (null !== $days) {
|
||||
$cutoffDate = new \DateTimeImmutable(sprintf('-%d days', (int) $days));
|
||||
$qb->andWhere('d.updatedAt < :cutoff')
|
||||
->setParameter('cutoff', $cutoffDate);
|
||||
}
|
||||
|
||||
if (null !== $bookingId) {
|
||||
$qb->andWhere('d.bookingId = :bookingId')
|
||||
->setParameter('bookingId', (int) $bookingId);
|
||||
}
|
||||
|
||||
$drafts = $qb->getQuery()->getResult();
|
||||
|
||||
if (0 === \count($drafts)) {
|
||||
$io->success('No drafts found matching the criteria.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$io->title(sprintf('Found %d draft(s)', \count($drafts)));
|
||||
|
||||
$rows = [];
|
||||
foreach ($drafts as $draft) {
|
||||
$formData = $draft->getFormData();
|
||||
$participantCount = isset($formData['participants']) ? \count($formData['participants']) : 0;
|
||||
|
||||
$rows[] = [
|
||||
$draft->getId(),
|
||||
$draft->getBookingId(),
|
||||
$draft->getUser()->getEmail(),
|
||||
$draft->getCreatedAt()->format('Y-m-d H:i:s'),
|
||||
$draft->getUpdatedAt()->format('Y-m-d H:i:s'),
|
||||
$this->getAgeDays($draft->getUpdatedAt()),
|
||||
$participantCount,
|
||||
];
|
||||
}
|
||||
|
||||
$io->table(
|
||||
['ID', 'Booking ID', 'User Email', 'Created At', 'Updated At', 'Days Old', 'Participants'],
|
||||
$rows
|
||||
);
|
||||
|
||||
$io->section('Investigation Tips');
|
||||
$io->listing([
|
||||
'Check logs for "Booking update successful" with matching booking_id after the draft\'s updated_at',
|
||||
'If a successful update exists after draft creation, the draft should have been deleted (potential bug)',
|
||||
'Drafts are preserved when: user cancels, API rejects submission, or user simply leaves',
|
||||
]);
|
||||
|
||||
if ($shouldDelete) {
|
||||
if (false === $io->confirm(sprintf('Are you sure you want to delete %d draft(s)?', \count($drafts)), false)) {
|
||||
$io->warning('Deletion cancelled.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$em = $this->draftRepository->getEntityManager();
|
||||
foreach ($drafts as $draft) {
|
||||
$em->remove($draft);
|
||||
}
|
||||
$em->flush();
|
||||
|
||||
$io->success(sprintf('Deleted %d draft(s).', \count($drafts)));
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function getAgeDays(\DateTimeImmutable $date): string
|
||||
{
|
||||
$now = new \DateTimeImmutable();
|
||||
$diff = $now->diff($date);
|
||||
|
||||
if ($diff->days > 0) {
|
||||
return sprintf('%d days', $diff->days);
|
||||
}
|
||||
|
||||
if ($diff->h > 0) {
|
||||
return sprintf('%d hours', $diff->h);
|
||||
}
|
||||
|
||||
return sprintf('%d min', $diff->i);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user