Failed Conditions
Push — master ( 36cd34...e25ade )
by
unknown
47:16 queued 16:37
created

DataImporter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace Spryker\Zed\DataImport\Business\Model;
9
10
use Countable;
11
use Exception;
12
use Generated\Shared\Transfer\DataImporterConfigurationTransfer;
13
use Generated\Shared\Transfer\DataImporterReportMessageTransfer;
14
use Generated\Shared\Transfer\DataImporterReportTransfer;
15
use Generator;
16
use Spryker\Shared\ErrorHandler\ErrorLogger;
17
use Spryker\Zed\DataImport\Business\DataImporter\DataImporterDataSetIdentifierAwareInterface;
18
use Spryker\Zed\DataImport\Business\DataImporter\DataImporterImportGroupAwareInterface;
19
use Spryker\Zed\DataImport\Business\Exception\DataImporterGeneratorException;
20
use Spryker\Zed\DataImport\Business\Exception\DataImportException;
21
use Spryker\Zed\DataImport\Business\Exception\DataSetException;
22
use Spryker\Zed\DataImport\Business\Exception\TransactionRolledBackAwareExceptionInterface;
23
use Spryker\Zed\DataImport\Business\Model\DataReader\ConfigurableDataReaderInterface;
24
use Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface;
25
use Spryker\Zed\DataImport\Business\Model\DataSet\DataSetInterface;
26
use Spryker\Zed\DataImport\Business\Model\DataSet\DataSetStepBrokerAwareInterface;
27
use Spryker\Zed\DataImport\Business\Model\DataSet\DataSetStepBrokerInterface;
28
use Spryker\Zed\DataImport\DataImportConfig;
29
use Spryker\Zed\DataImport\Dependency\Facade\DataImportToGracefulRunnerInterface;
30
31
class DataImporter implements
32
    DataImporterBeforeImportAwareInterface,
33
    DataImporterInterface,
34
    DataImporterAfterImportAwareInterface,
35
    DataSetStepBrokerAwareInterface,
36
    DataImporterImportGroupAwareInterface,
37
    DataImporterDataSetIdentifierAwareInterface
38
{
39
    /**
40
     * @var string
41
     */
42
    public const KEY_CONTEXT = 'CONTEXT';
43
44
    /**
45
     * @var string
46
     */
47
    protected $importType;
48
49
    /**
50
     * @var \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface
51
     */
52
    protected $dataReader;
53
54
    /**
55
     * @var array<\Spryker\Zed\DataImport\Business\Model\DataImporterBeforeImportInterface>
56
     */
57
    protected $beforeImportHooks = [];
58
59
    /**
60
     * @var array<\Spryker\Zed\DataImport\Business\Model\DataImporterAfterImportInterface>
61
     */
62
    protected $afterImportHooks = [];
63
64
    /**
65
     * @var array<\Spryker\Zed\DataImport\Business\Model\DataSet\DataSetStepBrokerInterface>
66
     */
67
    protected $dataSetStepBroker = [];
68
69
    /**
70
     * @var string
71
     */
72
    protected $importGroup;
73
74
    /**
75
     * @var \Spryker\Zed\DataImport\Dependency\Facade\DataImportToGracefulRunnerInterface
76
     */
77
    protected $gracefulRunnerFacade;
78
79
    /**
80
     * @var string|null
81
     */
82
    protected ?string $dataSetIdentifierKey = null;
83
84
    /**
85
     * @param string $importType
86
     * @param \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface $dataReader
87
     * @param \Spryker\Zed\DataImport\Dependency\Facade\DataImportToGracefulRunnerInterface $gracefulRunnerFacade
88
     */
89
    public function __construct($importType, DataReaderInterface $dataReader, DataImportToGracefulRunnerInterface $gracefulRunnerFacade)
90
    {
91
        $this->importType = $importType;
92
        $this->dataReader = $dataReader;
93
        $this->gracefulRunnerFacade = $gracefulRunnerFacade;
94
    }
95
96
    /**
97
     * @param \Spryker\Zed\DataImport\Business\Model\DataSet\DataSetStepBrokerInterface $dataSetStepBroker
98
     *
99
     * @return $this
100
     */
101
    public function addDataSetStepBroker(DataSetStepBrokerInterface $dataSetStepBroker)
102
    {
103
        $this->dataSetStepBroker[] = $dataSetStepBroker;
104
105
        return $this;
106
    }
107
108
    /**
109
     * @param \Spryker\Zed\DataImport\Business\Model\DataImporterBeforeImportInterface $beforeImportHook
110
     *
111
     * @return $this
112
     */
113
    public function addBeforeImportHook(DataImporterBeforeImportInterface $beforeImportHook)
114
    {
115
        $this->beforeImportHooks[] = $beforeImportHook;
116
117
        return $this;
118
    }
119
120
    /**
121
     * @param \Spryker\Zed\DataImport\Business\Model\DataImporterAfterImportInterface $afterImportHook
122
     *
123
     * @return $this
124
     */
125
    public function addAfterImportHook(DataImporterAfterImportInterface $afterImportHook)
126
    {
127
        $this->afterImportHooks[] = $afterImportHook;
128
129
        return $this;
130
    }
131
132
    /**
133
     * @return void
134
     */
135
    public function beforeImport()
136
    {
137
        foreach ($this->beforeImportHooks as $beforeImportHook) {
138
            $beforeImportHook->beforeImport();
139
        }
140
    }
141
142
    /**
143
     * @param string $dataSetIdentifierKey
144
     *
145
     * @return void
146
     */
147
    public function setDataSetIdentifierKey(string $dataSetIdentifierKey): void
148
    {
149
        $this->dataSetIdentifierKey = $dataSetIdentifierKey;
150
    }
151
152
    /**
153
     * {@inheritDoc}
154
     *
155
     * @param \Generated\Shared\Transfer\DataImporterConfigurationTransfer|null $dataImporterConfigurationTransfer
156
     *
157
     * @return \Generated\Shared\Transfer\DataImporterReportTransfer
158
     */
159
    public function import(?DataImporterConfigurationTransfer $dataImporterConfigurationTransfer = null)
160
    {
161
        $start = microtime(true);
162
163
        $dataImporterReportTransfer = $this->importByDataImporterConfiguration($dataImporterConfigurationTransfer);
164
        $dataImporterReportTransfer->setImportTime(microtime(true) - $start);
165
166
        $this->afterImport();
167
168
        return $dataImporterReportTransfer;
169
    }
170
171
    /**
172
     * @param \Generated\Shared\Transfer\DataImporterConfigurationTransfer|null $dataImporterConfigurationTransfer
173
     *
174
     * @return \Generated\Shared\Transfer\DataImporterReportTransfer
175
     */
176
    protected function importByDataImporterConfiguration(
177
        ?DataImporterConfigurationTransfer $dataImporterConfigurationTransfer = null
178
    ): DataImporterReportTransfer {
179
        $dataReader = $this->getDataReader($dataImporterConfigurationTransfer);
180
        $source = $this->getSourceFromDataImporterConfigurationTransfer($dataImporterConfigurationTransfer);
181
        $dataImporterReportTransfer = $this->prepareDataImportReport($dataReader, $source);
182
183
        $this->beforeImport();
184
185
        $dataImportGenerator = $this->createDataImportGenerator($dataReader, $dataImporterReportTransfer, $dataImporterConfigurationTransfer);
186
187
        $this->gracefulRunnerFacade->run($dataImportGenerator, DataImporterGeneratorException::class);
188
189
        return $dataImportGenerator->getReturn();
190
    }
191
192
    /**
193
     * This method is turned into a `\Generator` by using the `yield` operator. Every iteration of it will be fully
194
     * completed until a signal was received.
195
     *
196
     * @param \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface $dataReader
197
     * @param \Generated\Shared\Transfer\DataImporterReportTransfer $dataImporterReportTransfer
198
     * @param \Generated\Shared\Transfer\DataImporterConfigurationTransfer|null $dataImporterConfigurationTransfer
199
     *
200
     * @throws \Spryker\Zed\DataImport\Business\Exception\DataImportException
201
     *
202
     * @return \Generator<\Generated\Shared\Transfer\DataImporterReportTransfer|null>
203
     */
204
    protected function createDataImportGenerator(
205
        DataReaderInterface $dataReader,
206
        DataImporterReportTransfer $dataImporterReportTransfer,
207
        ?DataImporterConfigurationTransfer $dataImporterConfigurationTransfer = null
208
    ): Generator {
209
        try {
210
            foreach ($dataReader as $dataSet) {
211
                yield;
212
213
                $dataSet[static::KEY_CONTEXT] = $dataImporterConfigurationTransfer?->getContext();
214
215
                try {
216
                    $this->processDataSet($dataSet, $dataImporterReportTransfer);
217
                } catch (Exception $dataImportException) {
218
                    if ($dataImportException instanceof TransactionRolledBackAwareExceptionInterface) {
219
                        $dataImporterReportTransfer = $this->recalculateImportedDataSetCount($dataImporterReportTransfer, $dataImportException);
220
                    }
221
                    $exceptionMessage = $this->buildExceptionMessage($dataImportException, $dataImporterReportTransfer->getImportedDataSetCount() + 1);
222
223
                    if ($dataImporterConfigurationTransfer && $dataImporterConfigurationTransfer->getThrowException()) {
224
                        throw new DataImportException($exceptionMessage, 0, $dataImportException);
225
                    }
226
227
                    ErrorLogger::getInstance()->log($dataImportException);
228
229
                    $dataImporterReportMessageTransfer = $this->createDataSetExceptionReportMessage(
230
                        $dataImportException,
231
                        $dataReader,
232
                        $dataSet,
233
                    );
234
235
                    $dataImporterReportTransfer
236
                        ->setIsSuccess(false)
237
                        ->addMessage($dataImporterReportMessageTransfer);
238
                }
239
240
                unset($dataSet);
241
            }
242
        } catch (DataSetException $exception) {
243
            $dataImporterReportMessageTransfer = (new DataImporterReportMessageTransfer())
244
                ->setMessage($exception->getMessage())
245
                ->setDataSetNumber($dataReader->key());
246
247
            if ($exception->findError()) {
248
                $dataImporterReportMessageTransfer->setError($exception->findError());
249
            }
250
251
            $dataImporterReportTransfer
252
                ->setIsSuccess(false)
253
                ->addMessage($dataImporterReportMessageTransfer);
254
        } catch (DataImporterGeneratorException $exception) {
255
        }
256
257
        return $dataImporterReportTransfer;
258
    }
259
260
    /**
261
     * @param \Spryker\Zed\DataImport\Business\Model\DataSet\DataSetInterface $dataSet
262
     * @param \Generated\Shared\Transfer\DataImporterReportTransfer $dataImporterReportTransfer
263
     *
264
     * @return void
265
     */
266
    protected function processDataSet(DataSetInterface $dataSet, DataImporterReportTransfer $dataImporterReportTransfer): void
267
    {
268
        $this->importDataSet($dataSet);
269
        $dataImporterReportTransfer->setImportedDataSetCount($dataImporterReportTransfer->getImportedDataSetCount() + 1);
270
    }
271
272
    /**
273
     * @param \Generated\Shared\Transfer\DataImporterReportTransfer $dataImporterReportTransfer
274
     * @param \Spryker\Zed\DataImport\Business\Exception\TransactionRolledBackAwareExceptionInterface $exception
275
     *
276
     * @return \Generated\Shared\Transfer\DataImporterReportTransfer
277
     */
278
    protected function recalculateImportedDataSetCount(
279
        DataImporterReportTransfer $dataImporterReportTransfer,
280
        TransactionRolledBackAwareExceptionInterface $exception
281
    ): DataImporterReportTransfer {
282
        if ($dataImporterReportTransfer->getImportedDataSetCount() === 0) {
283
            return $dataImporterReportTransfer;
284
        }
285
286
        $dataImporterReportTransfer->setImportedDataSetCount($dataImporterReportTransfer->getImportedDataSetCount() - $exception->getRolledBackRowsCount());
287
288
        return $dataImporterReportTransfer;
289
    }
290
291
    /**
292
     * @return void
293
     */
294
    public function afterImport()
295
    {
296
        foreach ($this->afterImportHooks as $afterImportHook) {
297
            $afterImportHook->afterImport();
298
        }
299
    }
300
301
    /**
302
     * @param \Spryker\Zed\DataImport\Business\Model\DataSet\DataSetInterface $dataSet
303
     *
304
     * @return void
305
     */
306
    protected function importDataSet(DataSetInterface $dataSet)
307
    {
308
        foreach ($this->dataSetStepBroker as $dataSetStep) {
309
            $dataSetStep->execute($dataSet);
310
        }
311
    }
312
313
    /**
314
     * {@inheritDoc}
315
     *
316
     * @return string
317
     */
318
    public function getImportType()
319
    {
320
        return $this->importType;
321
    }
322
323
    /**
324
     * @param string $importGroup
325
     *
326
     * @return void
327
     */
328
    public function setImportGroup(string $importGroup): void
329
    {
330
        $this->importGroup = $importGroup;
331
    }
332
333
    /**
334
     * @return string
335
     */
336
    public function getImportGroup(): string
337
    {
338
        return $this->importGroup ?: DataImportConfig::IMPORT_GROUP_FULL;
339
    }
340
341
    /**
342
     * @param \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface $dataReader
343
     * @param string|null $source
344
     *
345
     * @return \Generated\Shared\Transfer\DataImporterReportTransfer
346
     */
347
    protected function prepareDataImportReport(DataReaderInterface $dataReader, ?string $source = null)
348
    {
349
        $dataImporterReportTransfer = new DataImporterReportTransfer();
350
        $dataImporterReportTransfer
351
            ->setImportType($this->getImportType())
352
            ->setImportedDataSetCount(0)
353
            ->setIsSuccess(true)
354
            ->setIsReaderCountable(false)
355
            ->setSource($source);
356
357
        if ($dataReader instanceof Countable) {
358
            $dataImporterReportTransfer->setIsReaderCountable(true);
359
            $dataImporterReportTransfer->setExpectedImportableDataSetCount($dataReader->count());
360
        }
361
362
        return $dataImporterReportTransfer;
363
    }
364
365
    /**
366
     * @param \Generated\Shared\Transfer\DataImporterConfigurationTransfer|null $dataImporterConfigurationTransfer
367
     *
368
     * @return \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface
369
     */
370
    protected function getDataReader(?DataImporterConfigurationTransfer $dataImporterConfigurationTransfer = null)
371
    {
372
        if ($dataImporterConfigurationTransfer && $dataImporterConfigurationTransfer->getReaderConfiguration()) {
373
            if ($this->dataReader instanceof ConfigurableDataReaderInterface) {
374
                $this->dataReader->configure($dataImporterConfigurationTransfer->getReaderConfiguration());
375
            }
376
        }
377
378
        return $this->dataReader;
379
    }
380
381
    /**
382
     * @param \Exception $exception
383
     * @param int|null $dataSetPosition
384
     *
385
     * @return string
386
     */
387
    protected function buildExceptionMessage(Exception $exception, $dataSetPosition = null)
388
    {
389
        $message = $exception->getMessage() . PHP_EOL . PHP_EOL;
390
        if ($dataSetPosition && $this->getImportType() !== 'full') {
391
            $message .= sprintf('DataImport for "%s" at data set position "%s" has an error.', $this->getImportType(), $dataSetPosition) . PHP_EOL . PHP_EOL;
392
            $message .= sprintf('For debugging execute "vendor/bin/console data:import:%s -o %s -l 1 -t"', $this->getImportType(), $dataSetPosition) . PHP_EOL . PHP_EOL;
393
        }
394
395
        $message .= sprintf('%s:%s %s', $exception->getFile(), $exception->getLine(), PHP_EOL . PHP_EOL . $exception->getTraceAsString());
396
397
        return $message;
398
    }
399
400
    /**
401
     * @param \Generated\Shared\Transfer\DataImporterConfigurationTransfer|null $dataImporterConfigurationTransfer
402
     *
403
     * @return string|null
404
     */
405
    protected function getSourceFromDataImporterConfigurationTransfer(?DataImporterConfigurationTransfer $dataImporterConfigurationTransfer): ?string
406
    {
407
        if ($dataImporterConfigurationTransfer && $dataImporterConfigurationTransfer->getReaderConfiguration()) {
408
            return $dataImporterConfigurationTransfer->getReaderConfiguration()->getFileName();
409
        }
410
411
        return null;
412
    }
413
414
    /**
415
     * @param \Exception $dataImportException
416
     * @param \Spryker\Zed\DataImport\Business\Model\DataReader\DataReaderInterface $dataReader
417
     * @param \Spryker\Zed\DataImport\Business\Model\DataSet\DataSetInterface $dataSet
418
     *
419
     * @return \Generated\Shared\Transfer\DataImporterReportMessageTransfer
420
     */
421
    protected function createDataSetExceptionReportMessage(
422
        Exception $dataImportException,
423
        DataReaderInterface $dataReader,
424
        DataSetInterface $dataSet
425
    ): DataImporterReportMessageTransfer {
426
        $dataSetIdentifier = $dataSet[$this->dataSetIdentifierKey] ?? null;
427
428
        $dataImporterReportMessageTransfer = (new DataImporterReportMessageTransfer())
429
            ->setMessage($dataImportException->getMessage())
430
            ->setDataSetNumber($dataReader->key())
431
            ->setDataSetIdentifier($dataSetIdentifier);
432
433
        $previousException = $dataImportException->getPrevious();
434
        if ($previousException instanceof DataImportException && $previousException->findError()) {
435
            $dataImporterReportMessageTransfer->setError($previousException->findError());
436
        }
437
438
        return $dataImporterReportMessageTransfer;
439
    }
440
}
441