>> ot:cron clean-temp-files */ class CleanTempFiles extends BaseCronJob implements CronjobInterface { /** * Delay before removing a temporary file (in days) */ private const DELETE_OLDER_THAN = 7; /** * ID of the Access who author the file deletion */ private const AUTHOR = 10984; /** * Delay before deleting the record of the temporary file from the DB */ private const PURGE_RECORDS_OLDER_THAN = 365; /** * @param FileRepository $fileRepository * @param EntityManagerInterface $em * @param AccessRepository $accessRepository * @param LocalStorage $storage */ #[Pure] public function __construct( private FileRepository $fileRepository, private EntityManagerInterface $em, private AccessRepository $accessRepository, private LocalStorage $storage ) { parent::__construct(); } /** * Preview the result of the execution, without actually deleting anything * @throws Exception */ public function preview(): void { $files = $this->listFilesToDelete(); $total = count($files); $this->ui->print($total . " temporary files to be removed"); $this->ui->print("> Printing the first and last 50 :"); $i = 0; foreach ($files as $file) { $i++; if ($i > 50 && ($total - $i) > 50) { continue; } if (($total - $i) === 50) { $this->ui->print(' (...)'); } $this->ui->print(' * ' . $file->getPath()); } $this->purgeDb(false); } /** * Proceed to the deletion of the files and the purge of the DB * @throws Exception */ public function execute(): void { $files = $this->listFilesToDelete(); $this->deleteFiles($files); $this->purgeDb(); } /** * List the files to delete in the DB * * @return Collection * @throws Exception */ protected function listFilesToDelete(): Collection { $maxDate = DatesUtils::new(); $maxDate->sub(new \DateInterval('P' . self::DELETE_OLDER_THAN . 'D')); $this->ui->print('List temporary files created before ' . $maxDate->format('c')); $criteria = new Criteria(); $criteria->where( Criteria::expr()?->andX( Criteria::expr()?->eq('isTemporaryFile', true), Criteria::expr()?->orX( Criteria::expr()?->lt('createDate', $maxDate), Criteria::expr()?->isNull('createDate') ) ) ); return $this->fileRepository->matching($criteria); } /** * Delete the files * * @param Collection $files */ protected function deleteFiles(Collection $files): void { $total = count($files); $this->ui->print($total . " temporary files to be removed"); $author = $this->accessRepository->find(self::AUTHOR); if ($author === null) { throw new \RuntimeException('Access ' . self::AUTHOR . ' could not be found'); } $this->ui->print('Deleting files...'); $i = 0; $deleted = 0; $this->ui->progress(0, $total); foreach ($files as $file) { try { $i++; $this->ui->progress($i, $total); $this->storage->delete($file, $author); $deleted++; } catch (\RuntimeException $e) { $this->ui->print('ERROR : ' . $e->getMessage()); } } $this->ui->print($deleted . ' files deleted'); } /** * Purge the DB from temporary file records older than N days * * @param bool $commit * @throws Exception */ protected function purgeDb(bool $commit = true): void { $maxDate = DatesUtils::new(); $maxDate->sub(new \DateInterval('P' . self::PURGE_RECORDS_OLDER_THAN . 'D')); $this->ui->print('Purge DB from records of files deleted before ' . $maxDate->format('c')); $this->em->beginTransaction(); $queryBuilder = $this->em->createQueryBuilder(); $q = $queryBuilder ->delete('File', 'f') ->where($queryBuilder->expr()->eq('f.isTemporaryFile', 1)) ->andWhere($queryBuilder->expr()->lt('f.updateDate', ':maxDate')) ->setParameter('maxDate', $maxDate) ->getQuery(); $purged = $q->getResult(); if ($commit) { $this->em->commit(); $this->ui->print('DB purged - ' . $purged . ' records permanently deleted'); return; } $this->em->rollback(); $this->ui->print('DB purged - ' . $purged . ' records would be permanently deleted'); } }