BaseExporter.php 7.3 KB

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