BaseExporter.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Service\Export;
  4. use App\ApiResources\Export\ExportRequest;
  5. use App\Entity\Core\File;
  6. use App\Enum\Core\FileTypeEnum;
  7. use App\Enum\Core\FileVisibilityEnum;
  8. use App\Repository\Access\AccessRepository;
  9. use App\Repository\Core\FileRepository;
  10. use App\Service\Export\Model\ExportModelInterface;
  11. use App\Service\File\FileManager;
  12. use App\Service\ServiceIterator\EncoderIterator;
  13. use App\Service\Utils\FileUtils;
  14. use App\Service\Utils\StringsUtils;
  15. use Doctrine\ORM\EntityManagerInterface;
  16. use Psr\Log\LoggerInterface;
  17. use Symfony\Contracts\Service\Attribute\Required;
  18. use Twig\Environment;
  19. /**
  20. * Classe de base des services d'export.
  21. */
  22. abstract class BaseExporter
  23. {
  24. // dependencies injections
  25. protected AccessRepository $accessRepository;
  26. protected FileRepository $fileRepository;
  27. protected Environment $twig;
  28. protected EncoderIterator $encoderIterator;
  29. protected EntityManagerInterface $entityManager;
  30. protected LoggerInterface $logger;
  31. protected FileManager $fileManager;
  32. protected FileUtils $fileUtils;
  33. #[Required]
  34. public function setAccessRepository(AccessRepository $accessRepository): void
  35. {
  36. $this->accessRepository = $accessRepository;
  37. }
  38. #[Required]
  39. public function setFileRepository(FileRepository $fileRepository): void
  40. {
  41. $this->fileRepository = $fileRepository;
  42. }
  43. #[Required]
  44. public function setTwig(Environment $twig): void
  45. {
  46. $this->twig = $twig;
  47. }
  48. #[Required]
  49. public function setEncoderIterator(EncoderIterator $encoderIterator): void
  50. {
  51. $this->encoderIterator = $encoderIterator;
  52. }
  53. #[Required]
  54. public function setEntityManager(EntityManagerInterface $entityManager): void
  55. {
  56. $this->entityManager = $entityManager;
  57. }
  58. #[Required]
  59. public function setFileManager(FileManager $fileManager): void
  60. {
  61. $this->fileManager = $fileManager;
  62. }
  63. #[Required]
  64. public function setLogger(LoggerInterface $logger): void
  65. {
  66. $this->logger = $logger;
  67. }
  68. #[Required]
  69. public function setFileUtils(FileUtils $fileUtils): void
  70. {
  71. $this->fileUtils = $fileUtils;
  72. }
  73. public function support(ExportRequest $exportRequest): bool
  74. {
  75. return false;
  76. }
  77. /**
  78. * Exécute l'opération d'export correspondant à la requête passée
  79. * en paramètre.
  80. */
  81. public function export(ExportRequest $exportRequest): File
  82. {
  83. $requesterId = $exportRequest->getRequesterId();
  84. $requester = $this->accessRepository->find($requesterId);
  85. if ($requester === null) {
  86. throw new \RuntimeException('Unable to determine the user; abort.');
  87. }
  88. // Génère le modèle à partir de l'exportRequest
  89. $model = $this->buildModel($exportRequest);
  90. // Génère le html à partir du template et du service
  91. $html = $this->render($model);
  92. // Encode le html au format voulu
  93. $content = $this->encode($html, $exportRequest->getFormat()->value);
  94. // Met à jour ou créé l'enregistrement du fichier en base
  95. if ($exportRequest->getFileId() !== null) {
  96. $file = $this->fileRepository->find($exportRequest->getFileId());
  97. } else {
  98. // Todo: voir si ce else est nécessaire une fois tous les exports implémentés (c'est
  99. // juste au cas où le record File n'existe pas encore en base)
  100. $organization = $requester->getOrganization();
  101. if ($organization === null) {
  102. throw new \RuntimeException('Unable to determine the organization of the curent user; abort.');
  103. }
  104. $file = $this->prepareFile($exportRequest, false);
  105. }
  106. return $this->fileManager->write($file, $content, $requester);
  107. }
  108. /**
  109. * Create a pending file record in the database.
  110. */
  111. public function prepareFile(ExportRequest $exportRequest, bool $flushFile = true): File
  112. {
  113. $requesterId = $exportRequest->getRequesterId();
  114. $requester = $this->accessRepository->find($requesterId);
  115. if ($requester === null) {
  116. throw new \RuntimeException('Unable to determine the current user; abort.');
  117. }
  118. $filename = $this->getFileBasename($exportRequest);
  119. if (!preg_match('/^.+\.'.$exportRequest->getFormat()->value.'$/i', $filename)) {
  120. $filename .= '.'.$exportRequest->getFormat()->value;
  121. }
  122. return $this->fileManager->prepareFile(
  123. $requester,
  124. $filename,
  125. $this->getFileType(),
  126. $requester,
  127. true,
  128. FileVisibilityEnum::NOBODY,
  129. $this->fileUtils->getMimeTypeFromExt($exportRequest->getFormat()->value),
  130. $flushFile
  131. );
  132. }
  133. /**
  134. * Construit le modèle de données qui servira au render du template.
  135. */
  136. public function buildModel(ExportRequest $exportRequest): ExportModelInterface
  137. {
  138. throw new \RuntimeException('not implemented error');
  139. }
  140. /**
  141. * Retourne le nom par défaut de cet export,
  142. * utilisé pour trouver le template twig ou encore pour nommer
  143. * le fichier exporté.
  144. */
  145. protected function getBasename(): string
  146. {
  147. $arr = explode('\\', static::class);
  148. $classname = end($arr);
  149. return StringsUtils::camelToSnake(
  150. preg_replace(
  151. '/^([\w]+)Exporter$/',
  152. '$1',
  153. $classname,
  154. 1)
  155. );
  156. }
  157. /**
  158. * Return the path of the twig template for this export.
  159. */
  160. protected function getTemplatePath(): string
  161. {
  162. return '@templates/export/'.$this->getBasename().'.html.twig';
  163. }
  164. /**
  165. * Génère le rendu HTML à partir du template twig et du modèle de données.
  166. *
  167. * Attention : Les liens vers les fichiers statiques ou dynamiques doivent être des chemins d'accès absolus pour
  168. * être traités par wkhtmltoX; Les images peuvent être incluses au format base64;
  169. *
  170. * @see App\Service\Twig\AssetsExtension
  171. *
  172. * @return string Rendu HTML
  173. */
  174. protected function render(ExportModelInterface $model): string
  175. {
  176. try {
  177. return $this->twig->render(
  178. $this->getTemplatePath(),
  179. ['model' => $model]
  180. );
  181. } catch (\Twig\Error\LoaderError|\Twig\Error\RuntimeError|\Twig\Error\SyntaxError $e) {
  182. throw new \RuntimeException('error during template rendering : '.$e);
  183. }
  184. }
  185. /**
  186. * Encode le html au format demandé.
  187. *
  188. * @param string $format @see ExportFormatEnum
  189. */
  190. protected function encode(string $html, string $format): string
  191. {
  192. return $this->encoderIterator->getEncoderFor($format)->encode($html);
  193. }
  194. /**
  195. * Retourne le nom du fichier exporté.
  196. */
  197. protected function getFileBasename(ExportRequest $exportRequest): string
  198. {
  199. return $this->getBasename();
  200. }
  201. /**
  202. * Retourne le type de fichier tel qu'il apparait au niveau du champ File.type.
  203. */
  204. protected function getFileType(): FileTypeEnum
  205. {
  206. return FileTypeEnum::UNKNOWN;
  207. }
  208. }