NOSQLActiveRecord::prepareData()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
namespace NOSQL\Models;
3
4
use MongoDB\BSON\ObjectId;
5
use MongoDB\BulkWriteResult;
6
use MongoDB\Database;
7
use NOSQL\Dto\Model\NOSQLModelDto;
8
use NOSQL\Exceptions\NOSQLValidationException;
9
use NOSQL\Models\base\NOSQLModelTrait;
10
use NOSQL\Models\base\NOSQLParserTrait;
11
use NOSQL\Services\ParserService;
12
use PSFS\base\Logger;
13
use PSFS\base\types\traits\SingletonTrait;
14
15
/**
16
 * Class NOSQLActiveRecord
17
 * @package NOSQL\Models
18
 */
19
abstract class NOSQLActiveRecord {
20
    use NOSQLModelTrait;
21
    use NOSQLParserTrait;
22
    use SingletonTrait;
23
24
    /**
25
     * NOSQLActiveRecord constructor.
26
     * @throws \NOSQL\Exceptions\NOSQLParserException
27
     * @throws \PSFS\base\exception\GeneratorException
28
     */
29
    public function __construct()
30
    {
31
        $this->hydrate();
32
    }
33
34
    /**
35
     * @return array
36
     */
37
    public function toArray() {
38
        return $this->dto->toArray();
39
    }
40
41
    /**
42
     * @param bool $cleanPk
43
     * @return \NOSQL\Dto\Model\NOSQLModelDto
44
     */
45
    public function getDtoCopy($cleanPk = false) {
46
        $copy = clone $this->dto;
47
        if($cleanPk) {
48
            $this->dto->resetPk();
49
        }
50
        return $copy;
51
    }
52
53
    private function prepareData() {
54
        $this->dto->validate(true);
55
    }
56
57
    /**
58
     * @param Database|null $con
59
     * @return bool
60
     */
61
    public function save(Database $con = null) {
62
        $saved = false;
63
        if(null === $con) {
64
            $con = ParserService::getInstance()->createConnection($this->getDomain());
65
        }
66
        $collection = $con->selectCollection($this->getSchema()->name);
67
        try {
68
            $isInsert = $isUpdate = false;
69
            $this->prepareData();
70
            $this->dto->setLastUpdate();
71
            if($this->isNew()) {
72
                $this->preInsert($con);
73
                $isInsert = true;
74
            } elseif ($this->isModified()) {
75
                $this->preUpdate($con);
76
                $isUpdate = true;
77
            }
78
            $result = $collection->insertOne($this->toArray());
79
            if($result->getInsertedCount() > 0) {
80
                $id = $result->getInsertedId();
81
                $this->dto->setPk($id->jsonSerialize()['$oid']);
82
                if($isInsert) {
83
                    $this->postInsert($con);
84
                } elseif($isUpdate) {
85
                    $this->postUpdate($con);
86
                }
87
                $saved = true;
88
                $this->countAction();
89
            }
90
        } catch(\Exception $exception) {
91
            if($exception instanceof NOSQLValidationException) {
92
                throw $exception;
93
            } else {
94
                Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
95
            }
96
        }
97
        return $saved;
98
    }
99
100
    /**
101
     * @param Database|null $con
102
     * @return bool
103
     */
104
    public function update(Database $con = null) {
105
        $updated = false;
106
        if(null === $con) {
107
            $con = ParserService::getInstance()->createConnection($this->getDomain());
108
        }
109
        $collection = $con->selectCollection($this->getSchema()->name);
110
        try {
111
            $this->prepareData();
112
            $this->dto->setLastUpdate();
113
            $this->preUpdate($con);
114
            $data = $this->toArray();
115
            unset($data['_id']);
116
            $collection->findOneAndReplace(['_id' => new ObjectId($this->dto->getPk())], $data);
117
            $this->postUpdate($con);
118
            $updated = true;
119
            $this->countAction();
120
        } catch(\Exception $exception) {
121
            if($exception instanceof NOSQLValidationException) {
122
                throw $exception;
123
            } else {
124
                Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
125
            }
126
        }
127
        return $updated;
128
    }
129
130
    /**
131
     * @param array $data
132
     * @param Database|null $con
133
     * @return int
134
     */
135
    public function bulkInsert(array $data, Database $con = null) {
136
        $inserts = 0;
137
        if(null === $con) {
138
            $con = ParserService::getInstance()->createConnection($this->getDomain());
139
        }
140
        $collection = $con->selectCollection($this->getSchema()->name);
141
        try {
142
            [$dtos, $data] = $this->prepareInsertDtos($data, $con);
143
            $result = $collection->insertMany($data);
144
            $ids = $result->getInsertedIds();
145
            $inserts = $this->parseInsertedDtos($con, $ids, $dtos);
146
            $this->setActionCount($inserts);
147
        } catch(\Exception $exception) {
148
            Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
149
        }
150
        return $inserts;
151
    }
152
153
    /**
154
     * Function to make a bulk upsert of documents
155
     * @param array $data
156
     * @param string $id
157
     * @param Database|null $con
158
     * @return int
159
     */
160
    public function bulkUpsert(array $data, $id, Database $con = null) {
161
        if(null === $con) {
162
            $con = ParserService::getInstance()->createConnection($this->getDomain());
163
        }
164
        $collection = $con->selectCollection($this->getSchema()->name);
165
166
        $upserts = 0;
167
        $filter = $options = $operations = [];
168
        try {
169
            // Check index collation
170
            $indexes = $collection->listIndexes();
171
            foreach($indexes as $index) {
172
                $indexInfo = $index->__debugInfo();
173
                $keys = array_keys($index["key"]);
174
                if ((count($keys) === 1) && ($keys[0] === $id) && (array_key_exists("collation", $indexInfo))) {
175
                    $collation = $indexInfo["collation"];
176
                    $options["collation"] = ["locale" => $collation["locale"], "strength" => $collation["strength"]];
177
                    break;
178
                }
179
            }
180
181
            foreach($data as $item) {
182
                $filter[$id] = ['$eq' => $item[$id]];
183
                $update = [];
184
                $update['$set'] = $item;
185
                $options['upsert'] = true;
186
                $operation = [
187
                    "updateOne" => [$filter, $update, $options]
188
                ];
189
                $operations[] = $operation;
190
            }
191
            /** @var BulkWriteResult $result */
192
            $result = $collection->bulkWrite($operations);
193
            $upserts = $result->getModifiedCount();
194
        } catch (\Exception $exception) {
195
            Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
196
        }
197
198
        return $upserts;
199
    }
200
201
    /**
202
     * @param Database|null $con
203
     * @return bool
204
     */
205
    public function delete(Database $con = null) {
206
        $deleted = false;
207
        if(null === $con) {
208
            $con = ParserService::getInstance()->createConnection($this->getDomain());
209
        }
210
        $collection = $con->selectCollection($this->getSchema()->name);
211
        try {
212
            $this->preDelete($con);
213
            $collection->deleteOne(['_id' => new ObjectId($this->dto->getPk())]);
214
            $this->postDelete($con);
215
            $deleted = true;
216
            $this->dto = null;
217
            $this->countAction();
218
        } catch(\Exception $exception) {
219
            Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
220
        }
221
        return $deleted;
222
    }
223
224
    /**
225
     * Function to make a bulk delete of documents
226
     * @param array $filters
227
     * @param Database|null $con
228
     * @return int
229
     */
230
    public function bulkDelete(array $filters, Database $con = null) {
231
        $deletedCount = 0;
232
        if(null === $con) {
233
            $con = ParserService::getInstance()->createConnection($this->getDomain());
234
        }
235
        $collection = $con->selectCollection($this->getSchema()->name);
236
        try {
237
            $result = $collection->deleteMany($filters);
238
            $deletedCount = $result->getDeletedCount();
239
        } catch(\Exception $exception) {
240
            Logger::log($exception->getMessage(), LOG_CRIT, $this->toArray());
241
        }
242
        return $deletedCount;
243
    }
244
245
    /**
246
     * @param array $data
247
     * @param Database $con
248
     * @return array
249
     * @throws \NOSQL\Exceptions\NOSQLValidationException
250
     */
251
    private function prepareInsertDtos(array $data, Database $con)
252
    {
253
        $dtos = [];
254
        /** @var NOSQLModelDto $dto */
255
        foreach ($data as &$insertData) {
256
            if(is_object($insertData) && $insertData instanceof NOSQLModelDto) {
257
                $dto = clone $insertData;
258
            } else {
259
                $dto = $this->getDtoCopy(true);
260
                $dto->fromArray($insertData);
261
            }
262
            $dto->validate();
263
            $dto->setLastUpdate();
264
            self::invokeHook($this, $dto, 'preInsert', $con);
265
            self::invokeHook($this, $dto, 'preSave', $con);
266
            $dtos[] = $dto;
267
            $insertData = $dto->toArray();
268
        }
269
        unset($dto);
270
        return [$dtos, $data];
271
    }
272
273
    /**
274
     * @param Database $con
275
     * @param ObjectId[] $ids
276
     * @param NOSQLModelDto[] $dtos
277
     * @return int
278
     * @throws \NOSQL\Exceptions\NOSQLValidationException
279
     */
280
    private function parseInsertedDtos(Database $con, $ids, $dtos)
281
    {
282
        $inserts = 0;
283
        foreach ($ids as $index => $insertedId) {
284
            $id = is_string($insertedId) ? ['$oid' => $insertedId] : $insertedId->jsonSerialize();
285
            $dto = $dtos[$index];
286
            if($dto instanceof  NOSQLModelDto) {
287
                $dto->setPk($id['$oid']);
288
            } else {
289
290
            }
291
            self::invokeHook($this, $dtos[$index], 'postInsert', $con);
292
            self::invokeHook($this, $dtos[$index], 'postSave', $con);
293
            $inserts++;
294
        }
295
        return $inserts;
296
    }
297
}
298