BaseExporter.php 7.5 KB

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