• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

RonasIT / laravel-helpers / 15463936058

05 Jun 2025 09:48AM UTC coverage: 77.839% (+0.2%) from 77.591%
15463936058

Pull #202

github

web-flow
Merge db31ecfb3 into 829e475f7
Pull Request #202: feat: mysql driver for work with enums in migrations

13 of 13 new or added lines in 1 file covered. (100.0%)

25 existing lines in 2 files now uncovered.

1124 of 1444 relevant lines covered (77.84%)

12.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Importers/Importer.php
1
<?php
2

3
namespace RonasIT\Support\Importers;
4

5
use Illuminate\Support\Arr;
6
use RonasIT\Support\Iterators\CsvIterator;
7
use RonasIT\Support\Exceptions\IncorrectImportFileException;
8
use RonasIT\Support\Exceptions\IncorrectImportLineException;
9

10
class Importer
11
{
12
    public const DELETED_AT_FIELD = 'deleted_at';
13

14
    public const ITEMS_TO_CREATE = 'create';
15
    public const ITEMS_TO_UPDATE = 'update';
16

17
    public const CREATED_REPORTS = 'created';
18
    public const UPDATED_REPORTS = 'updated';
19

20
    protected $input;
21

22
    protected $service;
23
    protected $iterator;
24
    protected $exporter;
25

26
    protected $mandatoryFields = [];
27

28
    protected $items = [
29
        self::ITEMS_TO_CREATE => [],
30
        self::ITEMS_TO_UPDATE => [],
31
    ];
32

33
    protected $report = [
34
        self::UPDATED_REPORTS => 0,
35
        self::CREATED_REPORTS => 0,
36
        'errors' => [],
37
    ];
38

39
    public function setInput($input): self
40
    {
41
        $this->input = $input;
×
42

43
        return $this;
×
44
    }
45

46
    public function setService($service): self
47
    {
48
        $this->service = $service;
×
49

50
        return $this;
×
51
    }
52

53
    public function setExporter($exporter): self
54
    {
55
        $this->exporter = app($exporter);
×
56

57
        return $this;
×
58
    }
59

60
    public function import(): array
61
    {
62
        $this->prepare();
×
63

64
        $this->markAllLines();
×
65

66
        $this->resolve();
×
67

68
        return $this->report;
×
69
    }
70

71
    public function prepare(): self
72
    {
73
        $this->iterator = new CsvIterator($this->input);
×
74

75
        return $this;
×
76
    }
77

78
    protected function markAllLines(): void
79
    {
80
        $isFilterLine = true;
×
81

82
        foreach ($this->iterator->getGenerator() as $line) {
×
83
            if (!$isFilterLine) {
×
84
                try {
85
                    $csvData = $this->prepareLine($line);
×
86
                } catch (IncorrectImportLineException $exception) {
×
87
                    $this->addError($exception->getMessage());
×
88

89
                    continue;
×
90
                }
91

92
                $this->markForCreate($csvData);
×
93
                $this->markForUpdate($csvData);
×
94

95
                continue;
×
96
            }
97

98
            $fields = $this->prepareFields($line);
×
99

100
            if (head($fields) == 'id') {
×
101
                $isFilterLine = false;
×
102

103
                $this->iterator->parseColumns($fields);
×
104

105
                $this->validateHeader();
×
106
            }
107
        }
108
    }
109

110
    protected function resolve(): void
111
    {
112
        $this->createAllMarked();
×
113
        $this->updateAllMarked();
×
114
    }
115

116
    protected function prepareFields(array $line): array
117
    {
118
        return array_map(function ($field) {
×
119
            $field = strtolower($field);
×
120

121
            return str_replace('=EF=BB=BF', '', quoted_printable_encode($field));
×
122
        }, $line);
×
123
    }
124

125
    protected function prepareLine(array $line): array
126
    {
127
        if (empty($line['id'])) {
×
128
            $line['id'] = null;
×
129
        }
130

131
        if (Arr::has($line, self::DELETED_AT_FIELD) && empty($line[self::DELETED_AT_FIELD])) {
×
132
            $line[self::DELETED_AT_FIELD] = null;
×
133
        }
134

135
        return $line;
×
136
    }
137

138
    protected function markForCreate(array $line): void
139
    {
140
        if (!$this->isValidForCreation($line)) {
×
141
            return;
×
142
        }
143

144
        $this->items[self::ITEMS_TO_CREATE][] = $line;
×
145
    }
146

147
    protected function isValidForCreation(array $line): bool
148
    {
149
        if (empty($line['id'])) {
×
150
            return true;
×
151
        }
152

153
        return !$this->service->withTrashed()->exists(['id' => $line['id']]);
×
154
    }
155

156
    protected function markForUpdate(array $line): void
157
    {
158
        if ($this->isValidForUpdating($line)) {
×
159
            $this->items[self::ITEMS_TO_UPDATE][$line['id']] = $line;
×
160
        }
161
    }
162

163
    protected function isValidForUpdating(array $line): bool
164
    {
165
        if (empty($line['id']) || in_array($line, $this->items[self::ITEMS_TO_UPDATE])) {
×
166
            return false;
×
167
        }
168

169
        $diff = $this->getDiff($line);
×
170

171
        return !empty($diff);
×
172
    }
173

174
    protected function addError(string $error): void
175
    {
176
        $this->report['errors'][] = $this->formatError($error);
×
177
    }
178

179
    protected function formatError(string $error): string
180
    {
181
        $lineNumber = $this->iterator->key() + 1;
×
182

183
        return "Line {$lineNumber}: {$error}";
×
184
    }
185

186
    protected function validateDuplicatingOfId(array $item): bool
187
    {
188
        if (empty($item['id'])) {
×
189
            return false;
×
190
        }
191

192
        return $this->service->withTrashed()->exists([
×
193
            'id' => $item['id'],
×
194
        ]);
×
195
    }
196

197
    protected function createAllMarked(): void
198
    {
199
        foreach ($this->items[self::ITEMS_TO_CREATE] as $item) {
×
200
            $this->service->create($item);
×
201

202
            $this->report[self::CREATED_REPORTS]++;
×
203
        }
204
    }
205

206
    protected function updateAllMarked(): void
207
    {
208
        foreach ($this->items[self::ITEMS_TO_UPDATE] as $id => $item) {
×
209
            $this->service->update(['id' => $id], $item);
×
210

211
            $this->report[self::UPDATED_REPORTS]++;
×
212
        }
213
    }
214

215
    protected function getDiff(array $item): array
216
    {
217
        $itemFromDB = $this->service->first(['id' => $item['id']]);
×
218

219
        $exportedLine = $this->exporter->getLine($itemFromDB);
×
220
        $importedLine = array_values($this->iterator->current());
×
221

222
        return array_diff($exportedLine, $importedLine);
×
223
    }
224

225
    protected function validateHeader(): void
226
    {
227
        $line = $this->iterator->current();
×
228

229
        $mandatoryValues = Arr::only($line, $this->mandatoryFields);
×
230

231
        if (count($mandatoryValues) != count($this->mandatoryFields)) {
×
232
            $notExistedFields = array_filter($this->mandatoryFields, function ($field) use ($line) {
×
233
                return !Arr::has($line, $field);
×
234
            });
×
235

236
            if (count($notExistedFields) == 1) {
×
237
                $message = 'Mandatory field ' . head($notExistedFields) . ' is not provided in csv file';
×
238
            } else {
239
                $message = 'Mandatory fields ' . implode(', ', $notExistedFields) . ' are not provided in csv file';
×
240
            }
241

242
            throw new IncorrectImportFileException($message);
×
243
        }
244
    }
245
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc