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:21
created

BulkSimpleEntityCreator::reset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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