Test Failed
Push — master ( 7cde39...b3d10e )
by Mauro
05:55
created

Importer::executeSingleInsertQueries()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 0
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the DbImporter package.
4
 *
5
 * (c) Mauro Cassani<https://github.com/mauretto78>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace DbImporter;
12
13
use DbImporter\Exceptions\NotAllowedDriverException;
14
use DbImporter\Exceptions\NotAllowedModeException;
15
use DbImporter\Exceptions\NotIterableDataException;
16
use DbImporter\Normalizer\StdClassNormalizer;
17
use DbImporter\QueryBuilder\Contracts\QueryBuilderInterface;
18
use DbImporter\QueryBuilder\MySqlQueryBuilder;
19
use DbImporter\QueryBuilder\PgSqlQueryBuilder;
20
use DbImporter\QueryBuilder\SqliteQueryBuilder;
21
use Doctrine\Common\Collections\ArrayCollection;
22
use Doctrine\DBAL\Connection;
23
use Doctrine\DBAL\Statement;
24
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
25
use Symfony\Component\Serializer\Serializer;
26
27
class Importer
28
{
29
    /**
30
     * Allowed drivers
31
     */
32
    const ALLOWED_DRIVERS = [
33
        'pdo_mysql',
34
        'pdo_pgsql',
35
        'pdo_sqlite',
36
    ];
37
38
    /**
39
     * Allowed insert modes
40
     */
41
    const ALLOWED_INSERT_MODES = [
42
        'single',
43
        'multiple',
44
    ];
45
46
    /**
47
     * @var Connection $dbal
48
     */
49
    private $dbal;
50
51
    /**
52
     * @var string
53
     */
54
    private $driver;
55
56
    /**
57
     * @var string
58
     */
59
    private $table;
60
61
    /**
62
     * @var bool
63
     */
64
    private $ignoreErrors;
65
66
    /**
67
     * @var array
68
     */
69
    private $mapping;
70
71
    /**
72
     * @var ArrayCollection
73
     */
74
    private $data;
75
76
    /**
77
     * @var string
78
     */
79
    private $mode;
80
81
    /**
82
     * Importer constructor.
83
     * @param Connection $dbal
84
     * @param $table
85
     * @param array $mapping
86
     * @param array $data
87
     * @param $skipDuplicates
88
     * @param string $mode
89
     */
90
    private function __construct(
91
        Connection $dbal,
92
        $table,
93
        $mapping,
94
        $data,
95
        $skipDuplicates,
96
        $mode = 'multiple'
97
    ) {
98
        $this->checkDriver($driver = $dbal->getDriver()->getName());
99
        $this->checkMode($mode);
100
        $this->dbal = $dbal;
101
        $this->driver = $driver;
102
        $this->table = $table;
103
        $this->mapping = $mapping;
104
        $this->data = $this->normalize($data);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->normalize($data) of type array is incompatible with the declared type Doctrine\Common\Collections\ArrayCollection of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
105
        $this->ignoreErrors = $skipDuplicates;
106
        $this->mode = $mode;
107
    }
108
109
    /**
110
     * @param $driver
111
     * @throws NotAllowedDriverException
112
     */
113
    private function checkDriver($driver)
114
    {
115
        if (false === in_array($driver, self::ALLOWED_DRIVERS)) {
116
            throw new NotAllowedDriverException(
117
                sprintf(
118
                    'The driver '.$driver.' is not allowed. Drivers allowed are: [%s]',
119
                    implode(',', self::ALLOWED_DRIVERS)
120
                )
121
            );
122
        }
123
    }
124
125
    /**
126
     * @param $mode
127
     * @throws NotAllowedModeException
128
     */
129
    private function checkMode($mode)
130
    {
131
        if (false === in_array($mode, self::ALLOWED_INSERT_MODES)) {
132
            throw new NotAllowedModeException(
133
                sprintf(
134
                    'The mode '.$mode.' is not allowed. Drivers allowed are: [%s]',
135
                    implode(',', self::ALLOWED_INSERT_MODES)
136
                )
137
            );
138
        }
139
    }
140
141
    /**
142
     * @param $data
143
     * @return array
144
     * @throws NotIterableDataException
145
     */
146
    private function normalize($data)
147
    {
148
        if (false === is_iterable($data)) {
149
            throw new NotIterableDataException('Data is not iterable');
150
        }
151
152
        $serializer = new Serializer([
153
            new StdClassNormalizer(),
154
            new ObjectNormalizer()
155
        ]);
156
157
        return $serializer->normalize($data);
158
    }
159
160
    /**
161
     * @param Connection $dbal
162
     * @param $table
163
     * @param $mapping
164
     * @param $data
165
     * @param $skipDuplicates
166
     * @param string $mode
167
     * @return Importer
168
     */
169
    public static function init(
170
        Connection $dbal,
171
        $table,
172
        $mapping,
173
        $data,
174
        $skipDuplicates,
175
        $mode = 'multiple'
176
    ) {
177
        return new self(
178
            $dbal,
179
            $table,
180
            $mapping,
181
            $data,
182
            $skipDuplicates,
183
            $mode
184
        );
185
    }
186
187
    /**
188
     * @return array
189
     */
190
    public function getQueries()
191
    {
192
        $queryBuilder = $this->getQueryBuilder();
193
194
        return (new $queryBuilder(
195
            $this->table,
196
            $this->mapping,
197
            $this->data,
198
            $this->ignoreErrors
199
        ))->getQueries($this->mode);
200
    }
201
202
    /**
203
     * @return string
204
     */
205
    private function getQueryBuilder()
206
    {
207
        switch ($this->driver) {
208
            case 'pdo_mysql':
209
                return MySqlQueryBuilder::class;
210
211
            case 'pdo_pgsql':
212
                return PgSqlQueryBuilder::class;
213
214
            case 'pdo_sqlite':
215
                return SqliteQueryBuilder::class;
216
        }
217
    }
218
219
    /**
220
     * @return bool
221
     */
222
    public function execute()
223
    {
224
        if ($this->mode == 'single') {
225
            return $this->executeSingleInsertQueries();
226
        }
227
228
        return $this->executeMultipleInsertQueries();
229
    }
230
231
    /**
232
     * @return bool
233
     */
234
    private function executeSingleInsertQueries()
235
    {
236
        $queries = $this->getQueries();
237
        $c = 0;
238
239
        foreach ($queries as $query) {
240
            $stmt = $this->dbal->prepare($query);
241
            $this->bindValuesToItem($this->data[$c], $stmt);
242
            $c++;
243
244
            if (false === $stmt->execute()) {
245
                return false;
246
            }
247
        }
248
249
        return true;
250
    }
251
252
    /**
253
     * @return bool
254
     */
255
    private function executeMultipleInsertQueries()
256
    {
257
        $queries = $this->getQueries();
258
259
        /** @var QueryBuilderInterface $queryBuilder */
260
        $queryBuilder = $this->getQueryBuilder();
261
        $limit = $queryBuilder::MULTIPLE_QUERY_IMPORT_LIMIT;
262
        $start = 0;
263
264
        foreach ($queries as $query) {
265
            $stmt = $this->dbal->prepare($query);
266
            $c = 1;
267
            $dataSliced = array_slice($this->data, ($start*$limit), $limit);
0 ignored issues
show
Bug introduced by
$this->data of type Doctrine\Common\Collections\ArrayCollection is incompatible with the type array expected by parameter $array of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
            $dataSliced = array_slice(/** @scrutinizer ignore-type */ $this->data, ($start*$limit), $limit);
Loading history...
268
269
            foreach ($dataSliced as $item) {
270
                $this->bindValuesToItem($item, $stmt, $c);
271
                $c++;
272
            }
273
274
            if (false === $stmt->execute()) {
275
                return false;
276
            }
277
278
            $start++;
279
        }
280
281
        return true;
282
    }
283
284
    /**
285
     * @param $item
286
     * @param $index
287
     * @param Statement $stmt
288
     */
289
    private function bindValuesToItem($item, Statement $stmt, $index = null)
290
    {
291
        foreach ($item as $key => $value) {
292
            $map = array_values($this->mapping);
293
294
            if (in_array($key, $map)) {
295
                $key = ($index) ? ':'.$key.'_'.$index : $key;
296
                $stmt->bindValue($key, $value);
297
            }
298
        }
299
    }
300
}
301