GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#154)
by joseph
22:43
created

BulkSimpleEntityCreator   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 106
dl 0
loc 256
ccs 0
cts 162
cp 0
rs 9.92
c 0
b 0
f 0
wmc 31

16 Methods

Rating   Name   Duplication   Size   Complexity  
A runPolyfillIfRequired() 0 6 2
A reset() 0 4 1
A doSave() 0 7 2
A addEntityCreationData() 0 4 1
A freeResources() 0 7 1
A appendToQuery() 0 3 1
A addEntityToSave() 0 3 1
A generateId() 0 9 2
B runQuery() 0 46 7
A __construct() 0 10 1
A setInsertMode() 0 11 3
A setHelper() 0 9 1
A isBinaryUuid() 0 5 1
A getQueryLine() 0 5 1
A buildSql() 0 18 3
A addEntitiesToSave() 0 8 3
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Savers;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Factories\UuidFactory;
8
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\Savers\BulkEntityUpdater\BulkSimpleEntityCreatorHelper;
10
use EdmondsCommerce\DoctrineStaticMeta\Schema\MysqliConnectionFactory;
11
use EdmondsCommerce\DoctrineStaticMeta\Schema\UuidFunctionPolyfill;
12
use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType;
13
14
class BulkSimpleEntityCreator extends AbstractBulkProcess
15
{
16
    public const INSERT_MODE_INSERT = 'INSERT ';
17
    public const INSERT_MODE_IGNORE = 'INSERT IGNORE ';
18
    public const INSERT_MODES       = [
19
        self::INSERT_MODE_INSERT,
20
        self::INSERT_MODE_IGNORE,
21
    ];
22
23
    /**
24
     * @var BulkSimpleEntityCreatorHelper
25
     */
26
    private $helper;
27
    /**
28
     * @var string
29
     */
30
    private $tableName;
31
    /**
32
     * @var string
33
     */
34
    private $entityFqn;
35
    /**
36
     * Is the UUID binary
37
     *
38
     * @var bool
39
     */
40
    private $isBinaryUuid = true;
41
    /**
42
     * @var ClassMetadataInfo
43
     */
44
    private $meta;
45
    /**
46
     * @var string
47
     */
48
    private $primaryKeyCol;
49
    /**
50
     * @var \mysqli
51
     */
52
    private $mysqli;
53
    /**
54
     * @var UuidFunctionPolyfill
55
     */
56
    private $uuidFunctionPolyfill;
57
    /**
58
     * @var UuidFactory
59
     */
60
    private $uuidFactory;
61
    /**
62
     * @var string
63
     */
64
    private $query;
65
    /**
66
     * For creation this should always be 100%, so 1
67
     *
68
     * @var int
69
     */
70
    private $requireAffectedRatio = 1;
71
    /**
72
     * @var int
73
     */
74
    private $totalAffectedRows = 0;
75
76
    private $insertMode = 'insert';
77
78
    public function __construct(
79
        EntityManagerInterface $entityManager,
80
        MysqliConnectionFactory $mysqliConnectionFactory,
81
        UuidFunctionPolyfill $uuidFunctionPolyfill,
82
        UuidFactory $uuidFactory
83
    ) {
84
        parent::__construct($entityManager);
85
        $this->mysqli               = $mysqliConnectionFactory->createFromEntityManager($entityManager);
86
        $this->uuidFunctionPolyfill = $uuidFunctionPolyfill;
87
        $this->uuidFactory          = $uuidFactory;
88
    }
89
90
91
    public function addEntityToSave(EntityInterface $entity)
92
    {
93
        throw new \RuntimeException('You should not try to save Entities with this saver');
94
    }
95
96
    public function addEntitiesToSave(array $entities)
97
    {
98
        foreach ($entities as $entityData) {
99
            if (\is_array($entityData)) {
100
                $this->addEntityCreationData($entityData);
101
                continue;
102
            }
103
            throw new \InvalidArgumentException('You should only pass in simple arrays of scalar entity data');
104
        }
105
    }
106
107
    public function addEntityCreationData(array $entityData)
108
    {
109
        $this->entitiesToSave[] = $entityData;
110
        $this->bulkSaveIfChunkBigEnough();
111
    }
112
113
    public function setHelper(BulkSimpleEntityCreatorHelper $helper): void
114
    {
115
        $this->helper        = $helper;
116
        $this->tableName     = $helper->getTableName();
117
        $this->entityFqn     = $helper->getEntityFqn();
118
        $this->meta          = $this->entityManager->getClassMetadata($this->entityFqn);
119
        $this->primaryKeyCol = $this->meta->getSingleIdentifierFieldName();
120
        $this->isBinaryUuid  = $this->isBinaryUuid();
121
        $this->runPolyfillIfRequired();
122
    }
123
124
    private function isBinaryUuid(): bool
125
    {
126
        $idMapping = $this->meta->getFieldMapping($this->meta->getSingleIdentifierFieldName());
127
128
        return $idMapping['type'] === UuidBinaryOrderedTimeType::NAME;
129
    }
130
131
    private function runPolyfillIfRequired(): void
132
    {
133
        if (false === $this->isBinaryUuid) {
134
            return;
135
        }
136
        $this->uuidFunctionPolyfill->run();
137
    }
138
139
    /**
140
     * @param string $insertMode
141
     *
142
     * @return BulkSimpleEntityCreator
143
     */
144
    public function setInsertMode(string $insertMode): BulkSimpleEntityCreator
145
    {
146
        if (false === \in_array($insertMode, self::INSERT_MODES, true)) {
147
            throw new \InvalidArgumentException('Invalid insert mode');
148
        }
149
        $this->insertMode = $insertMode;
150
        if ($this->insertMode === self::INSERT_MODE_IGNORE) {
151
            $this->requireAffectedRatio = 0;
152
        }
153
154
        return $this;
155
    }
156
157
    protected function freeResources()
158
    {
159
        /**
160
         * AS these are not actually entities, lets empty them out before free resources tries to detach from the entity manager
161
         */
162
        $this->entitiesToSave = [];
163
        parent::freeResources();
164
    }
165
166
    protected function doSave(): void
167
    {
168
        foreach ($this->entitiesToSave as $entityData) {
169
            $this->appendToQuery($this->buildSql($entityData));
170
        }
171
        $this->runQuery();
172
        $this->reset();
173
    }
174
175
    private function appendToQuery(string $sql)
176
    {
177
        $this->query .= "\n$sql";
178
    }
179
180
    private function buildSql(array $entityData): string
181
    {
182
        $sql  = $this->insertMode . " into {$this->tableName} set ";
183
        $sqls = [
184
            $this->primaryKeyCol . ' = ' . $this->generateId(),
185
        ];
186
        foreach ($entityData as $key => $value) {
187
            if ($key === $this->primaryKeyCol) {
188
                throw new \InvalidArgumentException(
189
                    'You should not pass in IDs, they will be auto generated'
190
                );
191
            }
192
            $value  = $this->mysqli->escape_string((string)$value);
193
            $sqls[] = "`$key` = '$value'";
194
        }
195
        $sql .= implode(', ', $sqls) . ';';
196
197
        return $sql;
198
    }
199
200
    private function generateId()
201
    {
202
        if ($this->isBinaryUuid) {
203
            $uuid = (string)$this->uuidFactory->getOrderedTimeUuid();
204
205
            return "UUID_TO_BIN('$uuid')";
206
        }
207
208
        return (string)$this->uuidFactory->getUuid();
209
    }
210
211
    private function runQuery(): void
212
    {
213
        if ('' === $this->query) {
214
            return;
215
        }
216
        $this->query = "
217
           START TRANSACTION;
218
           SET FOREIGN_KEY_CHECKS = 0; 
219
           SET UNIQUE_CHECKS = 0;
220
           {$this->query}             
221
           SET FOREIGN_KEY_CHECKS = 1; 
222
           SET UNIQUE_CHECKS = 1;
223
           COMMIT;";
224
        $result      = $this->mysqli->multi_query($this->query);
225
        if (true !== $result) {
226
            throw new \RuntimeException(
227
                'Multi Query returned false which means the first statement failed: ' .
228
                $this->mysqli->error
229
            );
230
        }
231
        $affectedRows = 0;
232
        $queryCount   = 0;
233
        do {
234
            $queryCount++;
235
            $errorNo = (int)$this->mysqli->errno;
236
            if (0 !== $errorNo) {
237
                $errorMessage = 'Query #' . $queryCount .
238
                                ' got MySQL Error #' . $errorNo .
239
                                ': ' . $this->mysqli->error
240
                                . "\nQuery: " . $this->getQueryLine($queryCount) . "'\n";
241
                throw new \RuntimeException($errorMessage);
242
            }
243
            $affectedRows += max($this->mysqli->affected_rows, 0);
244
            if (false === $this->mysqli->more_results()) {
245
                break;
246
            }
247
            $this->mysqli->next_result();
248
        } while (true);
249
        if ($affectedRows < count($this->entitiesToSave) * $this->requireAffectedRatio) {
250
            throw new \RuntimeException(
251
                'Affected rows count of ' . $affectedRows .
252
                ' does match the expected count of entitiesToSave ' . count($this->entitiesToSave)
253
            );
254
        }
255
        $this->totalAffectedRows += $affectedRows;
256
        $this->mysqli->commit();
257
    }
258
259
    private function getQueryLine(int $line): string
260
    {
261
        $lines = explode(";\n", $this->query);
262
263
        return $lines[$line + 1];
264
    }
265
266
    private function reset()
267
    {
268
        $this->entityCreationData = [];
0 ignored issues
show
Bug Best Practice introduced by
The property entityCreationData does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
269
        $this->query              = '';
270
    }
271
272
}