Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
4.93% |
17 / 345 |
|
10.00% |
1 / 10 |
CRAP | |
0.00% |
0 / 1 |
AbstractGenerator | |
4.93% |
17 / 345 |
|
10.00% |
1 / 10 |
4280.76 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
17 / 17 |
|
100.00% |
1 / 1 |
1 | |||
setCollectivity | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
initializeDocument | |
0.00% |
0 / 68 |
|
0.00% |
0 / 1 |
20 | |||
addHomepage | |
0.00% |
0 / 70 |
|
0.00% |
0 / 1 |
30 | |||
addTableOfContent | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
createContentSection | |
0.00% |
0 / 49 |
|
0.00% |
0 / 1 |
182 | |||
generateResponse | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
addLinkedData | |
0.00% |
0 / 48 |
|
0.00% |
0 / 1 |
42 | |||
addTable | |
0.00% |
0 / 58 |
|
0.00% |
0 / 1 |
1332 | |||
getDate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | /** |
4 | * This file is part of the MADIS - RGPD Management application. |
5 | * |
6 | * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes |
7 | * |
8 | * This program is free software: you can redistribute it and/or modify |
9 | * it under the terms of the GNU Affero General Public License as published by |
10 | * the Free Software Foundation, either version 3 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU Affero General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Affero General Public License |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | declare(strict_types=1); |
23 | |
24 | namespace App\Domain\Reporting\Generator\Word; |
25 | |
26 | use App\Application\Symfony\Security\UserProvider; |
27 | use App\Domain\Registry\Model\Contractor; |
28 | use App\Domain\Registry\Model\Mesurement; |
29 | use App\Domain\Registry\Model\Proof; |
30 | use App\Domain\Registry\Model\Request; |
31 | use App\Domain\Registry\Model\Tool; |
32 | use App\Domain\Registry\Model\Treatment; |
33 | use App\Domain\Registry\Model\Violation; |
34 | use App\Domain\User\Dictionary\ContactCivilityDictionary; |
35 | use App\Domain\User\Model\Collectivity; |
36 | use PhpOffice\PhpWord\Element\Section; |
37 | use PhpOffice\PhpWord\IOFactory; |
38 | use PhpOffice\PhpWord\PhpWord; |
39 | use PhpOffice\PhpWord\Settings; |
40 | use PhpOffice\PhpWord\SimpleType\Jc; |
41 | use PhpOffice\PhpWord\SimpleType\TblWidth; |
42 | use PhpOffice\PhpWord\Style; |
43 | use PhpOffice\PhpWord\Style\Table; |
44 | use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
45 | use Symfony\Component\HttpFoundation\BinaryFileResponse; |
46 | use Symfony\Component\HttpFoundation\ResponseHeaderBag; |
47 | |
48 | abstract class AbstractGenerator implements GeneratorInterface |
49 | { |
50 | /** |
51 | * @var UserProvider |
52 | */ |
53 | protected $userProvider; |
54 | |
55 | /** |
56 | * @var ParameterBagInterface |
57 | */ |
58 | protected $parameterBag; |
59 | |
60 | /** |
61 | * @var array |
62 | */ |
63 | protected $tableStyle; |
64 | |
65 | /** |
66 | * @var array |
67 | */ |
68 | protected $cellHeadStyle; |
69 | |
70 | /** |
71 | * @var array |
72 | */ |
73 | protected $textHeadStyle; |
74 | |
75 | /** |
76 | * @var Collectivity |
77 | */ |
78 | protected $collectivity; |
79 | |
80 | protected ?string $title = null; |
81 | |
82 | /** |
83 | * AbstractGenerator constructor. |
84 | */ |
85 | public function __construct( |
86 | UserProvider $userProvider, |
87 | ParameterBagInterface $parameterBag, |
88 | ) { |
89 | $this->userProvider = $userProvider; |
90 | $this->parameterBag = $parameterBag; |
91 | |
92 | $this->tableStyle = [ |
93 | 'borderColor' => '006699', |
94 | 'borderSize' => 6, |
95 | 'cellMargin' => 100, |
96 | 'unit' => TblWidth::PERCENT, |
97 | 'width' => 100 * 50, |
98 | 'layout' => Table::LAYOUT_FIXED, |
99 | ]; |
100 | |
101 | $this->cellHeadStyle = [ |
102 | 'bgColor' => '3c8dbc', |
103 | ]; |
104 | |
105 | $this->textHeadStyle = [ |
106 | 'bold' => true, |
107 | 'color' => 'ffffff', |
108 | ]; |
109 | } |
110 | |
111 | public function setCollectivity(Collectivity $collectivity) |
112 | { |
113 | $this->collectivity = $collectivity; |
114 | } |
115 | |
116 | /** |
117 | * Initialize PHPWord document variables & default values. |
118 | */ |
119 | public function initializeDocument(PhpWord $document): void |
120 | { |
121 | if ($this->collectivity) { |
122 | $collectivity = $this->collectivity; |
123 | } else { |
124 | $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity(); |
125 | } |
126 | // CONFIGURATION |
127 | $document->getSettings()->setThemeFontLang(new Style\Language(Style\Language::FR_FR)); |
128 | $document->getSettings()->setUpdateFields(true); |
129 | |
130 | $document->setDefaultFontName('Verdana'); |
131 | $document->setDefaultFontSize(10); |
132 | $document->setDefaultParagraphStyle(['spaceAfter' => 200]); |
133 | |
134 | $document->addNumberingStyle('tableList', [ |
135 | 'type' => 'multilevel', |
136 | 'levels' => [ |
137 | ['format' => 'bullet', 'text' => 'ï‚·', 'suffix' => 'space', 'alignment' => 'left', 'hanging' => 0, 'left' => 0, 'tabPos' => 0, 'font' => 'Symbol'], |
138 | ['format' => 'bullet', 'text' => 'ï‚·', 'suffix' => 'space', 'alignment' => 'left', 'hanging' => 0, 'left' => 0, 'tabPos' => 0, 'font' => 'Symbol'], |
139 | ], |
140 | ]); |
141 | |
142 | $properties = $document->getDocInfo(); |
143 | if ($collectivity->getReviewData() && $collectivity->getReviewData()->getDocumentName()) { |
144 | $properties->setTitle($collectivity->getReviewData()->getDocumentName()); |
145 | } else { |
146 | $properties->setTitle('Bilan de gestion des données à caractère personnel'); |
147 | } |
148 | |
149 | // STYLE |
150 | // Numbered heading |
151 | $document->addNumberingStyle( |
152 | 'headingNumbering', |
153 | ['type' => 'multilevel', |
154 | 'levels' => [ |
155 | ['pStyle' => 'Heading1', 'format' => 'decimal', 'text' => '%1.'], |
156 | ['pStyle' => 'Heading2', 'format' => 'decimal', 'text' => '%1.%2.'], |
157 | ], |
158 | ] |
159 | ); |
160 | |
161 | // Title style |
162 | $document->addTitleStyle( |
163 | 1, |
164 | [ |
165 | 'allCaps' => true, |
166 | 'bold' => true, |
167 | 'size' => 16, |
168 | ], |
169 | [ |
170 | 'pageBreakBefore' => false, |
171 | 'numLevel' => 0, |
172 | 'numStyle' => 'headingNumbering', |
173 | 'spaceBefore' => 500, |
174 | 'spaceAfter' => 250, |
175 | ] |
176 | ); |
177 | $document->addTitleStyle( |
178 | 2, |
179 | [ |
180 | 'bold' => true, |
181 | 'size' => 12, |
182 | ], |
183 | [ |
184 | 'pageBreakBefore' => false, |
185 | 'numLevel' => 1, |
186 | 'numStyle' => 'headingNumbering', |
187 | 'spaceBefore' => 500, |
188 | 'spaceAfter' => 250, |
189 | ] |
190 | ); |
191 | $document->addTitleStyle( |
192 | 3, |
193 | [ |
194 | 'bold' => true, |
195 | ], |
196 | [ |
197 | 'pageBreakBefore' => false, |
198 | 'spaceBefore' => 500, |
199 | 'spaceAfter' => 250, |
200 | ] |
201 | ); |
202 | } |
203 | |
204 | /** |
205 | * Add PhpWord homepage. |
206 | */ |
207 | public function addHomepage(PhpWord $document, string $title): void |
208 | { |
209 | if ($this->collectivity) { |
210 | $collectivity = $this->collectivity; |
211 | } else { |
212 | $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity(); |
213 | } |
214 | |
215 | $legalManager = $collectivity->getLegalManager(); |
216 | $section = $document->addSection(); |
217 | $section->addText( |
218 | $title, |
219 | [ |
220 | 'bold' => true, |
221 | 'color' => '3c8dbc', |
222 | 'size' => 36, |
223 | ], |
224 | [ |
225 | 'alignment' => Jc::CENTER, |
226 | 'spaceBefore' => 1000, |
227 | ] |
228 | ); |
229 | $section->addText( |
230 | \utf8_decode((string) $collectivity), |
231 | [ |
232 | 'bold' => true, |
233 | 'italic' => true, |
234 | 'size' => 20, |
235 | ], |
236 | [ |
237 | 'alignment' => Jc::CENTER, |
238 | 'spaceBefore' => 1000, |
239 | ] |
240 | ); |
241 | |
242 | // TREATMENT RESPONSABLE |
243 | $section->addText( |
244 | 'Responsable du traitement', |
245 | ['bold' => true, 'size' => 15], |
246 | ['spaceBefore' => 1000] |
247 | ); |
248 | $section->addText( |
249 | ContactCivilityDictionary::getCivilities()[$legalManager->getCivility()] . ' ' . $legalManager->getFullName(), |
250 | ['size' => 12] |
251 | ); |
252 | |
253 | // DPO |
254 | $section->addText( |
255 | 'Délégué à la Protection des Données', |
256 | ['bold' => true, 'size' => 15], |
257 | ['spaceBefore' => 1000] |
258 | ); |
259 | |
260 | $hasDpo = $collectivity->isDifferentDpo(); |
261 | $dpoDefaultFullName = $this->parameterBag->get('APP_DPO_FIRST_NAME') . ' ' . $this->parameterBag->get('APP_DPO_LAST_NAME'); |
262 | $dpoDefaultStreetAddress = $this->parameterBag->get('APP_DPO_ADDRESS_STREET'); |
263 | $dpoDefaultZipCodeCity = $this->parameterBag->get('APP_DPO_ADDRESS_ZIP_CODE') . ' ' . \strtoupper($this->parameterBag->get('APP_DPO_ADDRESS_CITY')); |
264 | |
265 | $section->addText( |
266 | $hasDpo ? $collectivity->getDpo()->getFullName() : $dpoDefaultFullName, |
267 | ['size' => 12] |
268 | ); |
269 | $section->addText( |
270 | $hasDpo ? $collectivity->getAddress()->getLineOne() : $dpoDefaultStreetAddress, |
271 | ['size' => 12] |
272 | ); |
273 | $section->addText( |
274 | $hasDpo ? "{$collectivity->getAddress()->getZipCode()} {$collectivity->getAddress()->getCity()}" : $dpoDefaultZipCodeCity, |
275 | ['size' => 12] |
276 | ); |
277 | |
278 | // CNIL |
279 | $section->addText( |
280 | 'Déclaration CNIL', |
281 | ['bold' => true, 'size' => 15], |
282 | ['spaceBefore' => 1000] |
283 | ); |
284 | |
285 | $section->addText( |
286 | 'Numéro de désignation : ' . $collectivity->getNbrCnil(), |
287 | ['size' => 12] |
288 | ); |
289 | |
290 | $section->addTextBreak(3); |
291 | $section->addText(date('d/m/Y'), ['italic' => true], ['alignment' => Jc::CENTER]); |
292 | } |
293 | |
294 | /** |
295 | * Create a table of content. |
296 | * |
297 | * @param Section $section The section on which to add content |
298 | * @param int $maxDepth The max depth for TOC generation |
299 | */ |
300 | public function addTableOfContent(Section $section, int $maxDepth = 1) |
301 | { |
302 | $section->addText( |
303 | 'Table des matières', |
304 | [ |
305 | 'name' => 'Verdana', |
306 | 'size' => 16, |
307 | 'bold' => true, |
308 | ], |
309 | [ |
310 | 'alignment' => Jc::CENTER, |
311 | 'spaceAfter' => 500, |
312 | ] |
313 | ); |
314 | |
315 | $section->addTOC(['bold' => true], null, 1, $maxDepth); |
316 | $section->addPageBreak(); |
317 | } |
318 | |
319 | /** |
320 | * Create the section used to generate document. |
321 | * |
322 | * @param PhpWord $document The word document to use |
323 | * @param string $title The title to add in header |
324 | * |
325 | * @return Section The created section |
326 | */ |
327 | public function createContentSection(PhpWord $document, string $title): Section |
328 | { |
329 | $this->title = $title; |
330 | // Create section |
331 | $section = $document->addSection(); |
332 | |
333 | // add page header |
334 | $header = $section->addHeader(); |
335 | $table = $header->addTable([ |
336 | 'borderColor' => '000000', |
337 | 'borderSize' => 3, |
338 | 'cellMargin' => 100, |
339 | 'unit' => TblWidth::PERCENT, |
340 | 'width' => 100 * 50, |
341 | 'layout' => Table::LAYOUT_FIXED, |
342 | ]); |
343 | $row = $table->addRow(10, ['tblHeader' => true, 'cantsplit' => true]); |
344 | $cell = $row->addCell(70 * 50, ['alignment' => Jc::CENTER]); |
345 | $cell->addText($title, ['alignment' => Jc::CENTER, 'bold' => true]); |
346 | $cell = $row->addCell(30 * 50, ['alignment' => Jc::CENTER]); |
347 | $cell->addPreserveText('Page {PAGE}/{NUMPAGES}', ['alignment' => Jc::CENTER]); |
348 | |
349 | $header->addTextBreak(); |
350 | |
351 | // Add page footer |
352 | $footer = $section->addFooter(); |
353 | $table = $footer->addTable([ |
354 | 'borderColor' => 'FFFFFF', |
355 | 'borderTopColor' => '000000', |
356 | 'borderSize' => 6, |
357 | 'unit' => TblWidth::PERCENT, |
358 | 'width' => 100 * 50, |
359 | ]); |
360 | $r = $table->addRow(); |
361 | $cell = $r->addCell(); |
362 | if (isset($this->logoDir) |
363 | && $this->collectivity |
364 | && $this->collectivity->getReviewData() |
365 | && $this->collectivity->getReviewData()->isShowCollectivityLogoFooter() |
366 | && $this->collectivity->getReviewData()->getLogo() |
367 | && file_exists($this->logoDir . $this->collectivity->getReviewData()->getLogo()) |
368 | ) { |
369 | $cell->addImage(file_get_contents($this->logoDir . $this->collectivity->getReviewData()->getLogo()), [ |
370 | 'width' => 100, |
371 | 'alignment' => Jc::LEFT, |
372 | ]); |
373 | } |
374 | if (isset($this->logoDir) |
375 | && isset($this->dpdLogo) |
376 | && $this->collectivity |
377 | && $this->collectivity->getReviewData() |
378 | && $this->collectivity->getReviewData()->isShowDPDLogoFooter() |
379 | && file_exists($this->logoDir . '/' . $this->dpdLogo) |
380 | ) { |
381 | $cell = $r->addCell(); |
382 | $cell->addImage(file_get_contents($this->logoDir . '/' . $this->dpdLogo), [ |
383 | 'width' => 100, |
384 | 'alignment' => Jc::RIGHT, |
385 | ]); |
386 | } |
387 | |
388 | return $section; |
389 | } |
390 | |
391 | /** |
392 | * Generate the response |
393 | * - This response parse PhpWord to a Word document |
394 | * - Prepare it in a BinaryFileResponse. |
395 | * |
396 | * @param string $documentName The document name to use |
397 | * |
398 | * @throws \PhpOffice\PhpWord\Exception\Exception |
399 | * @throws \Exception |
400 | * |
401 | * @return BinaryFileResponse The response |
402 | */ |
403 | public function generateResponse(PhpWord $document, string $documentName): BinaryFileResponse |
404 | { |
405 | Settings::setOutputEscapingEnabled(true); |
406 | $objWriter = IOFactory::createWriter($document, 'Word2007'); |
407 | |
408 | $currentDate = (new \DateTimeImmutable())->format('Ymd'); |
409 | $fileName = "{$currentDate}-{$documentName}.doc"; |
410 | $tempFile = \tempnam(\sys_get_temp_dir(), $fileName); |
411 | |
412 | $objWriter->save($tempFile); |
413 | |
414 | // Create response and return it |
415 | $response = new BinaryFileResponse($tempFile); |
416 | $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); |
417 | $response->setContentDisposition( |
418 | ResponseHeaderBag::DISPOSITION_ATTACHMENT, |
419 | $fileName |
420 | ); |
421 | |
422 | return $response; |
423 | } |
424 | |
425 | protected function addLinkedData(Section $section, Treatment|Violation|Tool|Request|Proof|Mesurement|Contractor $object) |
426 | { |
427 | $treatments = array_map(function (Treatment $t) { return $t->__toString(); }, $object->getTreatments()->toArray()); |
428 | $requests = array_map(function (Request $t) { return $t->__toString(); }, (array) $object->getRequests()->toArray()); |
429 | $violations = array_map(function (Violation $t) { return $t->__toString(); }, (array) $object->getViolations()->toArray()); |
430 | $tools = array_map(function (Tool $t) { return $t->__toString(); }, (array) $object->getTools()->toArray()); |
431 | $proofs = array_map(function (Proof $t) { return $t->__toString(); }, (array) $object->getProofs()->toArray()); |
432 | $mesurements = array_map(function (Mesurement $t) { return $t->__toString(); }, (array) $object->getMesurements()->toArray()); |
433 | $contractors = array_map(function (Contractor $t) { return $t->__toString(); }, (array) $object->getContractors()->toArray()); |
434 | |
435 | $linkedData = [ |
436 | [ |
437 | 'Traitements', |
438 | [['list' => $treatments]], |
439 | ], |
440 | [ |
441 | 'Sous-traitants', |
442 | [['list' => $contractors]], |
443 | ], |
444 | [ |
445 | 'Logiciels et supports', |
446 | [['list' => $tools]], |
447 | ], |
448 | [ |
449 | 'Demandes', |
450 | [['list' => $requests]], |
451 | ], |
452 | [ |
453 | 'Violations', |
454 | [['list' => $violations]], |
455 | ], |
456 | [ |
457 | 'Preuves', |
458 | [['list' => $proofs]], |
459 | ], |
460 | [ |
461 | 'Actions de protection', |
462 | [['list' => $mesurements]], |
463 | ], |
464 | ]; |
465 | if ($object instanceof Treatment) { |
466 | unset($linkedData[1]); |
467 | unset($linkedData[2]); |
468 | $linkedData = array_values($linkedData); |
469 | } |
470 | if (!($object instanceof Treatment) && $object->getCollectivity() && !$object->getCollectivity()->isHasModuleTools()) { |
471 | unset($linkedData[2]); |
472 | $linkedData = array_values($linkedData); |
473 | } |
474 | |
475 | if ($object instanceof Tool) { |
476 | unset($linkedData[1]); |
477 | $linkedData = array_values($linkedData); |
478 | } |
479 | |
480 | $this->addTable($section, $linkedData, false, self::TABLE_ORIENTATION_VERTICAL); |
481 | } |
482 | |
483 | protected function addTable(Section $section, array $data = [], bool $header = false, string $orientation = self::TABLE_ORIENTATION_HORIZONTAL): void |
484 | { |
485 | $table = $section->addTable($this->tableStyle); |
486 | if ($header) { |
487 | $headersTable = $data[0]; |
488 | if (array_key_exists('data', $headersTable)) { |
489 | $headersTable = $headersTable['data']; |
490 | } |
491 | $table->addRow(null, ['tblHeader' => true, 'cantsplit' => true]); |
492 | foreach ($headersTable as $element) { |
493 | $cell = $table->addCell(2500, $this->cellHeadStyle); |
494 | if (is_array($element) && array_key_exists('text', $element)) { |
495 | $cell->addText($element['text'], $element['style']); |
496 | } else { |
497 | $cell->addText($element, $this->textHeadStyle); |
498 | } |
499 | } |
500 | unset($data[0]); |
501 | } |
502 | |
503 | foreach ($data as $nbLine => $line) { |
504 | $table->addRow(null, ['cantsplit' => true]); |
505 | $lineData = $line['data'] ?? $line; |
506 | $lineStyle = $line['style'] ?? null; |
507 | foreach ($lineData as $nbCol => $col) { |
508 | $col = \is_array($col) ? $col : [$col]; |
509 | |
510 | if ($header && self::TABLE_ORIENTATION_HORIZONTAL === $orientation && 0 === $nbLine |
511 | || $header && self::TABLE_ORIENTATION_VERTICAL === $orientation && 0 === $nbCol) { |
512 | $cell = $table->addCell(2500, $this->cellHeadStyle); |
513 | foreach ($col as $item) { |
514 | $cell->addText($item, $this->textHeadStyle); |
515 | } |
516 | } else { |
517 | if (0 === $nbCol && !$header) { |
518 | $cell = $table->addCell(2500, $this->cellHeadStyle); |
519 | $cell->addText($col[$nbCol], $this->textHeadStyle); |
520 | } else { |
521 | /* If a style for the cell is specified, it bypass the line style */ |
522 | $cell = $table->addCell(5000 / \count($lineData), $col['style'] ?? $lineStyle); |
523 | $columns = $col['content'] ?? $col; |
524 | if (isset($columns[0]) && (!\is_array($columns[0]) || isset($columns[0]['array']))) { |
525 | $textrun = $cell->addTextRun(); |
526 | } |
527 | foreach ($columns as $key => $item) { |
528 | // If item is simple text, there is no other configuration |
529 | if (!\is_array($item)) { |
530 | if (!isset($textrun)) { |
531 | $textrun = $cell->addTextRun(); |
532 | } |
533 | |
534 | if (0 != $key) { |
535 | $textrun->addTextBreak(); |
536 | } |
537 | |
538 | $textrun->addText($item); |
539 | continue; |
540 | } |
541 | /* If item is array, there is 2 possibility : |
542 | - this is an array of item to display in a single cell |
543 | - there is additionnal configuration */ |
544 | if (isset($item['array']) && null !== $item['array']) { |
545 | if (!isset($textrun)) { |
546 | $textrun = $cell->addTextRun(); |
547 | } |
548 | $textrun->addTextBreak(); |
549 | foreach ($item['array'] as $subItemKey => $subItem) { |
550 | $textrun->addText($subItem); |
551 | if ($subItemKey !== count($item['array']) - 1) { |
552 | $textrun->addTextBreak(2); |
553 | } |
554 | } |
555 | } elseif (isset($item['list']) && is_array($item['list'])) { |
556 | foreach ($item['list'] as $subItem) { |
557 | if ($subItem) { |
558 | $cell->addListItem($subItem); |
559 | } |
560 | } |
561 | } else { |
562 | if (!isset($textrun)) { |
563 | $textrun = $cell->addTextRun(); |
564 | } |
565 | if (0 != $key) { |
566 | $textrun->addTextBreak(); |
567 | } |
568 | $textrun->addText( |
569 | $item['text'] ?? '', |
570 | $item['style'] ?? [] |
571 | ); |
572 | } |
573 | } |
574 | } |
575 | } |
576 | } |
577 | } |
578 | } |
579 | |
580 | /** |
581 | * Format a date for Word document. |
582 | * |
583 | * @param \DateTimeInterface|null $dateTime The date to parse |
584 | * |
585 | * @throws \Exception |
586 | * |
587 | * @return string The parsed date with the good timezone |
588 | */ |
589 | protected function getDate(?\DateTimeInterface $dateTime, ?string $format = null): string |
590 | { |
591 | if (\is_null($dateTime)) { |
592 | return ''; |
593 | } |
594 | |
595 | $format = $format ?? self::DATE_TIME_FORMAT; |
596 | |
597 | $parsedDateTime = new \DateTimeImmutable(); |
598 | $parsedDateTime = $parsedDateTime->setTimestamp($dateTime->getTimestamp()); |
599 | $parsedDateTime = $parsedDateTime->setTimezone(new \DateTimeZone(self::DATE_TIME_ZONE)); |
600 | |
601 | return $parsedDateTime->format($format); |
602 | } |
603 | } |