1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Application\Service; |
6
|
|
|
|
7
|
|
|
use Application\Enum\Precision; |
8
|
|
|
use Application\Enum\Site; |
9
|
|
|
use Application\Handler\TemplateHandler; |
10
|
|
|
use Application\Model\AbstractModel; |
11
|
|
|
use Application\Model\Card; |
12
|
|
|
use Application\Model\Collection; |
13
|
|
|
use Application\Model\Country; |
14
|
|
|
use Application\Model\DocumentType; |
15
|
|
|
use Application\Model\Domain; |
16
|
|
|
use Application\Model\Material; |
17
|
|
|
use Application\Model\Period; |
18
|
|
|
use Application\Repository\CountryRepository; |
19
|
|
|
use Application\Repository\DocumentTypeRepository; |
20
|
|
|
use Application\Repository\DomainRepository; |
21
|
|
|
use Application\Repository\MaterialRepository; |
22
|
|
|
use Application\Repository\PeriodRepository; |
23
|
|
|
use Ecodev\Felix\Api\Exception; |
24
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; |
25
|
|
|
use PhpOffice\PhpSpreadsheet\IOFactory; |
26
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; |
27
|
|
|
use Psr\Http\Message\UploadedFileInterface; |
28
|
|
|
use Throwable; |
29
|
|
|
|
30
|
|
|
class Importer |
31
|
|
|
{ |
32
|
|
|
/** |
33
|
|
|
* @var string[] |
34
|
|
|
*/ |
35
|
|
|
private readonly array $domains; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var string[] |
39
|
|
|
*/ |
40
|
|
|
private readonly array $documentTypes; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var string[] |
44
|
|
|
*/ |
45
|
|
|
private readonly array $countries; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var string[] |
49
|
|
|
*/ |
50
|
|
|
private readonly array $materials; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @var string[] |
54
|
|
|
*/ |
55
|
|
|
private readonly array $periods; |
56
|
|
|
|
57
|
|
|
private ?Collection $collection = null; |
58
|
|
|
|
59
|
5 |
|
public function __construct(private readonly Site $site) |
60
|
|
|
{ |
61
|
|
|
/** @var DomainRepository $domainRepository */ |
62
|
5 |
|
$domainRepository = _em()->getRepository(Domain::class); |
63
|
5 |
|
$this->domains = $domainRepository->getFullNames($this->site); |
|
|
|
|
64
|
|
|
|
65
|
|
|
/** @var PeriodRepository $periodRepository */ |
66
|
5 |
|
$periodRepository = _em()->getRepository(Period::class); |
67
|
5 |
|
$this->periods = $periodRepository->getFullNames($this->site); |
|
|
|
|
68
|
|
|
|
69
|
|
|
/** @var MaterialRepository $materialRepository */ |
70
|
5 |
|
$materialRepository = _em()->getRepository(Material::class); |
71
|
5 |
|
$this->materials = $materialRepository->getFullNames($this->site); |
|
|
|
|
72
|
|
|
|
73
|
|
|
/** @var CountryRepository $countryRepository */ |
74
|
5 |
|
$countryRepository = _em()->getRepository(Country::class); |
75
|
5 |
|
$this->countries = $countryRepository->getNames(); |
|
|
|
|
76
|
|
|
|
77
|
|
|
/** @var DocumentTypeRepository $documentTypeRepository */ |
78
|
5 |
|
$documentTypeRepository = _em()->getRepository(DocumentType::class); |
79
|
5 |
|
$this->documentTypes = $documentTypeRepository->getNames($this->site); |
|
|
|
|
80
|
|
|
} |
81
|
|
|
|
82
|
5 |
|
public function import(UploadedFileInterface $file, array $files, ?Collection $collection): array |
83
|
|
|
{ |
84
|
5 |
|
$this->collection = $collection; |
85
|
5 |
|
$tempFile = tempnam('data/tmp/', 'import'); |
86
|
5 |
|
$file->moveTo($tempFile); |
87
|
5 |
|
$spreadsheet = IOFactory::load($tempFile); |
88
|
5 |
|
$sheet = $spreadsheet->getSheet(0); |
89
|
|
|
|
90
|
5 |
|
$this->assertHeaders($sheet); |
91
|
4 |
|
$cards = $this->importSheet($sheet, $files); |
92
|
1 |
|
unlink($tempFile); |
93
|
|
|
|
94
|
1 |
|
return $cards; |
95
|
|
|
} |
96
|
|
|
|
97
|
5 |
|
private function assertHeaders(Worksheet $sheet): void |
98
|
|
|
{ |
99
|
5 |
|
$col = 1; |
100
|
5 |
|
$row = 1; |
101
|
5 |
|
foreach (TemplateHandler::HEADERS as $header) { |
102
|
5 |
|
$actual = $sheet->getCell([$col, $row])->getValue(); |
103
|
5 |
|
if ($actual !== $header) { |
104
|
1 |
|
$this->throwException($col, $row, 'S\'attend à "' . $header . '", mais a vu "' . $actual . '"'); |
105
|
|
|
} |
106
|
4 |
|
++$col; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
4 |
|
private function importSheet(Worksheet $sheet, array $images): array |
111
|
|
|
{ |
112
|
4 |
|
$imagesToImport = $this->indexByName($images); |
113
|
|
|
|
114
|
4 |
|
$col = 1; |
115
|
4 |
|
$row = 2; |
116
|
|
|
|
117
|
4 |
|
$cards = []; |
118
|
4 |
|
while ($imageName = (string) $sheet->getCell([$col, $row])->getValue()) { |
119
|
3 |
|
$imageNameWithoutExtension = pathinfo($imageName, PATHINFO_FILENAME); |
120
|
3 |
|
if (!array_key_exists($imageNameWithoutExtension, $imagesToImport)) { |
|
|
|
|
121
|
1 |
|
$this->throwException($col, $row, 'Image présente dans le fichier Excel, mais pas retrouvée dans les images uploadées: ' . $imageName); |
122
|
|
|
} |
123
|
|
|
|
124
|
2 |
|
$image = $imagesToImport[$imageNameWithoutExtension]; |
125
|
2 |
|
$cards[] = $this->importOne($sheet, $row, $image); |
126
|
|
|
|
127
|
1 |
|
unset($imagesToImport[$imageNameWithoutExtension]); |
128
|
|
|
|
129
|
1 |
|
++$row; |
130
|
|
|
} |
131
|
|
|
|
132
|
2 |
|
if (count($imagesToImport) > 1) { |
133
|
1 |
|
$message = count($imagesToImport) . ' images ont été uploadé pour lesquelles aucune information ont été trouvée dans le fichier Excel: ' . implode(', ', array_keys($imagesToImport)); |
134
|
1 |
|
$this->throwException($col, $row, $message); |
135
|
|
|
} |
136
|
|
|
|
137
|
1 |
|
return $cards; |
138
|
|
|
} |
139
|
|
|
|
140
|
4 |
|
private function throwException(int $column, int $row, string $message, ?Throwable $previousException = null): never |
141
|
|
|
{ |
142
|
4 |
|
$cell = Coordinate::stringFromColumnIndex($column) . $row; |
143
|
4 |
|
$message = 'Erreur dans la cellule ' . $cell . ': ' . $message; |
144
|
|
|
|
145
|
4 |
|
throw new Exception($message, 0, $previousException); |
146
|
|
|
} |
147
|
|
|
|
148
|
4 |
|
private function indexByName(array $images): array |
149
|
|
|
{ |
150
|
4 |
|
$imagesToImport = []; |
151
|
4 |
|
foreach ($images as $image) { |
152
|
3 |
|
$filename = pathinfo($image->getClientFilename(), PATHINFO_FILENAME); |
153
|
3 |
|
$imagesToImport[$filename] = $image; |
154
|
|
|
} |
155
|
|
|
|
156
|
4 |
|
return $imagesToImport; |
157
|
|
|
} |
158
|
|
|
|
159
|
2 |
|
private function importOne(Worksheet $sheet, int $row, UploadedFileInterface $image): Card |
160
|
|
|
{ |
161
|
2 |
|
$card = new Card(); |
162
|
2 |
|
$card->setSite($this->site); |
163
|
2 |
|
if ($this->collection) { |
164
|
1 |
|
$card->addCollection($this->collection); |
165
|
|
|
} |
166
|
|
|
|
167
|
2 |
|
_em()->persist($card); |
168
|
2 |
|
$col = 2; |
169
|
|
|
|
170
|
2 |
|
$card->setName($this->readString($sheet, $col++, $row)); |
171
|
2 |
|
$card->setExpandedName($this->readString($sheet, $col++, $row)); |
172
|
2 |
|
$domain = $this->readDomain($sheet, $col++, $row); |
173
|
1 |
|
if ($domain) { |
|
|
|
|
174
|
1 |
|
$card->addDomain($domain); |
175
|
|
|
} |
176
|
|
|
|
177
|
1 |
|
$material = $this->readMaterial($sheet, $col++, $row); |
178
|
1 |
|
if ($material) { |
|
|
|
|
179
|
1 |
|
$card->addMaterial($material); |
180
|
|
|
} |
181
|
|
|
|
182
|
1 |
|
$period = $this->readPeriod($sheet, $col++, $row); |
183
|
1 |
|
if ($period) { |
|
|
|
|
184
|
1 |
|
$card->addPeriod($period); |
185
|
|
|
} |
186
|
|
|
|
187
|
1 |
|
$card->setFrom($this->readInt($sheet, $col++, $row)); |
188
|
1 |
|
$card->setTo($this->readInt($sheet, $col++, $row)); |
189
|
1 |
|
$card->setCountry($this->readCountry($sheet, $col++, $row)); |
190
|
1 |
|
$card->setLocality($this->readString($sheet, $col++, $row)); |
191
|
1 |
|
$card->setProductionPlace($this->readString($sheet, $col++, $row)); |
192
|
1 |
|
$card->setObjectReference($this->readString($sheet, $col++, $row)); |
193
|
1 |
|
$card->setDocumentType($this->readDocumentType($sheet, $col++, $row)); |
194
|
1 |
|
$card->setTechniqueAuthor($this->readString($sheet, $col++, $row)); |
195
|
1 |
|
$card->setTechniqueDate($this->readString($sheet, $col++, $row)); |
196
|
1 |
|
$card->setLatitude($this->readFloat($sheet, $col++, $row)); |
197
|
1 |
|
$card->setLongitude($this->readFloat($sheet, $col++, $row)); |
198
|
1 |
|
$card->setPrecision($this->readPrecision($sheet, $col++, $row)); |
199
|
|
|
|
200
|
|
|
try { |
201
|
1 |
|
$card->setFile($image); |
202
|
|
|
} catch (Throwable $e) { |
203
|
|
|
$this->throwException($col, 1, 'Erreur avec l\'image', $e); |
204
|
|
|
} |
205
|
|
|
|
206
|
1 |
|
return $card; |
207
|
|
|
} |
208
|
|
|
|
209
|
2 |
|
private function readString(Worksheet $sheet, int $col, int $row): string |
210
|
|
|
{ |
211
|
2 |
|
return trim((string) $sheet->getCell([$col, $row])->getValue()); |
212
|
|
|
} |
213
|
|
|
|
214
|
2 |
|
private function readDomain(Worksheet $sheet, int $col, int $row): ?Domain |
215
|
|
|
{ |
216
|
|
|
/** @var null|Domain $result */ |
217
|
2 |
|
$result = $this->read($sheet, $col, $row, Domain::class, $this->domains, 'Domaine'); |
218
|
|
|
|
219
|
1 |
|
return $result; |
220
|
|
|
} |
221
|
|
|
|
222
|
1 |
|
private function readMaterial(Worksheet $sheet, int $col, int $row): ?Material |
223
|
|
|
{ |
224
|
|
|
/** @var null|Material $result */ |
225
|
1 |
|
$result = $this->read($sheet, $col, $row, Material::class, $this->materials, 'Materiel'); |
226
|
|
|
|
227
|
1 |
|
return $result; |
228
|
|
|
} |
229
|
|
|
|
230
|
1 |
|
private function readPeriod(Worksheet $sheet, int $col, int $row): ?Period |
231
|
|
|
{ |
232
|
|
|
/** @var null|Period $result */ |
233
|
1 |
|
$result = $this->read($sheet, $col, $row, Period::class, $this->periods, 'Période'); |
234
|
|
|
|
235
|
1 |
|
return $result; |
236
|
|
|
} |
237
|
|
|
|
238
|
1 |
|
private function readCountry(Worksheet $sheet, int $col, int $row): ?Country |
239
|
|
|
{ |
240
|
|
|
/** @var null|Country $result */ |
241
|
1 |
|
$result = $this->read($sheet, $col, $row, Country::class, $this->countries, 'Pays'); |
242
|
|
|
|
243
|
1 |
|
return $result; |
244
|
|
|
} |
245
|
|
|
|
246
|
1 |
|
private function readDocumentType(Worksheet $sheet, int $col, int $row): ?DocumentType |
247
|
|
|
{ |
248
|
|
|
/** @var null|DocumentType $result */ |
249
|
1 |
|
$result = $this->read($sheet, $col, $row, DocumentType::class, $this->documentTypes, 'Type de document'); |
250
|
|
|
|
251
|
1 |
|
return $result; |
252
|
|
|
} |
253
|
|
|
|
254
|
1 |
|
private function readPrecision(Worksheet $sheet, int $col, int $row): ?Precision |
255
|
|
|
{ |
256
|
1 |
|
$value = $sheet->getCell([$col, $row])->getValue(); |
257
|
1 |
|
if (!$value) { |
258
|
1 |
|
return null; |
259
|
|
|
} |
260
|
|
|
|
261
|
1 |
|
$value = Precision::tryFrom($value); |
262
|
1 |
|
if ($value) { |
263
|
1 |
|
return $value; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
$this->throwException($col, $row, 'Précision introuvable: ' . $value); |
267
|
|
|
} |
268
|
|
|
|
269
|
2 |
|
private function read(Worksheet $sheet, int $col, int $row, string $class, array $values, string $name): ?AbstractModel |
270
|
|
|
{ |
271
|
2 |
|
$value = $sheet->getCell([$col, $row])->getValue(); |
272
|
2 |
|
if (!$value) { |
273
|
1 |
|
return null; |
274
|
|
|
} |
275
|
|
|
|
276
|
2 |
|
if (array_key_exists($value, $values)) { |
277
|
1 |
|
return _em()->getRepository($class)->find($values[$value]); |
278
|
|
|
} |
279
|
|
|
|
280
|
1 |
|
$this->throwException($col, $row, $name . ' introuvable: ' . $value); |
281
|
|
|
} |
282
|
|
|
|
283
|
1 |
|
private function readInt(Worksheet $sheet, int $col, int $row): ?int |
284
|
|
|
{ |
285
|
1 |
|
$value = $sheet->getCell([$col, $row])->getValue(); |
286
|
1 |
|
if (!$value) { |
287
|
1 |
|
return null; |
288
|
|
|
} |
289
|
|
|
|
290
|
1 |
|
if (is_numeric($value)) { |
291
|
1 |
|
return (int) $value; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
$this->throwException($col, $row, 'N\'est pas un nombre entier: ' . $value); |
295
|
|
|
} |
296
|
|
|
|
297
|
1 |
|
private function readFloat(Worksheet $sheet, int $col, int $row): ?float |
298
|
|
|
{ |
299
|
1 |
|
$value = $sheet->getCell([$col, $row])->getValue(); |
300
|
1 |
|
if (!$value) { |
301
|
1 |
|
return null; |
302
|
|
|
} |
303
|
|
|
|
304
|
1 |
|
if (is_numeric($value)) { |
305
|
1 |
|
return (float) $value; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
$this->throwException($col, $row, 'N\'est pas un nombre à virgule: ' . $value); |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|