Completed
Push — master ( a3bb9e...9dbc80 )
by SignpostMarv
02:07
created

DaftObjectRepositoryByDaftObject()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 12
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 1
nop 2
dl 0
loc 12
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
* Base daft objects.
4
*
5
* @author SignpostMarv
6
*/
7
declare(strict_types=1);
8
9
namespace SignpostMarv\DaftObject;
10
11
use InvalidArgumentException;
12
use ParagonIE\EasyDB\EasyDB;
13
14
abstract class AbstractDaftObjectEasyDBRepository extends DaftObjectMemoryRepository
15
{
16
    /**
17
    * @var EasyDB
18
    */
19
    protected $db;
20
21
    /**
22
    * @param mixed ...$args
23
    */
24 14
    protected function __construct(string $type, EasyDB $db, ...$args)
25
    {
26 14
        parent::__construct($type, ...$args);
27 14
        $this->db = $db;
28 14
    }
29
30
    /**
31
    * {@inheritdoc}
32
    */
33 22
    public static function DaftObjectRepositoryByType(
34
        string $type,
35
        ...$args
36
    ) : DaftObjectRepository {
37
        /**
38
        * @var EasyDB|null
39
        */
40 22
        $db = array_shift($args) ?: null;
41
42 22
        $a = is_a($type, DaftObjectCreatedByArray::class, true);
43
        if (
44 22
            false === $a ||
45 22
            false === is_a($type, DefinesOwnIdPropertiesInterface::class, true)
46
        ) {
47 6
            throw new DaftObjectRepositoryTypeByClassMethodAndTypeException(
48 6
                1,
49 6
                static::class,
50 6
                __FUNCTION__,
51 6
                ($a ? DefinesOwnIdPropertiesInterface::class : DaftObjectCreatedByArray::class),
52 6
                $type
53
            );
54
        }
55
56 16
        $db = self::DaftObjectRepositoryArgsEasyDbActuallyRequired($db, 2, __FUNCTION__);
57
58 14
        return new static($type, $db, ...$args);
59
    }
60
61
    /**
62
    * {@inheritdoc}
63
    */
64 14
    public static function DaftObjectRepositoryByDaftObject(
65
        DefinesOwnIdPropertiesInterface $object,
66
        ...$args
67
    ) : DaftObjectRepository {
68
        /**
69
        * @var EasyDB|null
70
        */
71 14
        $db = array_shift($args) ?: null;
72
73 14
        $db = self::DaftObjectRepositoryArgsEasyDbActuallyRequired($db, 2, __FUNCTION__);
74
75 14
        return static::DaftObjectRepositoryByType(get_class($object), $db, ...$args);
76
    }
77
78
    /**
79
    * @param mixed $id
80
    */
81 4
    public function RemoveDaftObjectById($id) : void
82
    {
83 4
        $id = array_values(is_array($id) ? $id : [$id]);
84
85 4
        $idkv = self::DaftObjectIdPropertiesFromType($this->type, $id);
86
87 4
        $this->db->delete($this->DaftObjectDatabaseTable(), $this->ModifyTypesForDatabase($idkv));
88
89 4
        $this->ForgetDaftObjectById($id);
90 4
    }
91
92 6
    public function RememberDaftObjectData(
93
        DefinesOwnIdPropertiesInterface $object,
94
        bool $assumeDoesNotExist = false
95
    ) : void {
96 6
        $id = [];
97
98 6
        foreach ($object::DaftObjectIdProperties() as $prop) {
99
            /**
100
            * @var scalar|null|array|object
101
            */
102 6
            $propVal = $object->$prop;
103
104 6
            $id[$prop] = $propVal;
105
        }
106
107
        $this->db->tryFlatTransaction(function () use ($id, $object, $assumeDoesNotExist) : void {
108 6
            $exists = $assumeDoesNotExist ? false : $this->DaftObjectExistsInDatabase($id);
109 6
            $values = $this->RememberDaftObjectDataValues($object, $exists);
110 6
            $this->RememberDaftObjectDataUpdate($exists, $id, $values);
111 6
        });
112 6
    }
113
114
    /**
115
    * @param mixed $id
116
    *
117
    * @return array<string, mixed>
118
    */
119 8
    protected static function DaftObjectIdPropertiesFromType(string $type, $id) : array
120
    {
121 8
        if ( ! is_a($type, DefinesOwnIdPropertiesInterface::class, true)) {
122 2
            throw new InvalidArgumentException(
123
                'Argument 1 passed to ' .
124
                __METHOD__ .
125
                ' must be an implementation of ' .
126
                DefinesOwnIdPropertiesInterface::class .
127
                ', ' .
128 2
                $type .
129 2
                ' given!'
130
            );
131
        }
132
133
        /**
134
        * @var array<int, string>
135
        */
136 6
        $idProps = array_values((array) $type::DaftObjectIdProperties());
137
138 6
        if (is_scalar($id) && 1 === count($idProps)) {
139 2
            $id = [$id];
140
        }
141
142
        /**
143
        * @var array<string, mixed>
144
        */
145 6
        $idkv = [];
146
147 6
        if (is_array($id)) {
148 6
            foreach ($idProps as $i => $prop) {
149
                /**
150
                * @var scalar|null|array|object
151
                */
152 6
                $propVal = $id[$i];
153
154 6
                $idkv[$prop] = $propVal;
155
            }
156
        }
157
158 6
        return $idkv;
159
    }
160
161 16
    protected static function DaftObjectRepositoryArgsEasyDbActuallyRequired(
162
        ? EasyDB $db,
163
        int $arg,
164
        string $function
165
    ) : EasyDB {
166 16
        if (false === ($db instanceof EasyDB)) {
167 2
            throw new DatabaseConnectionNotSpecifiedException(
168 2
                $arg,
169 2
                static::class,
170 2
                $function,
171 2
                EasyDB::class,
172 2
                'null'
173
            );
174
        }
175
176 14
        return $db;
177
    }
178
179
    /**
180
    * @return array<string, mixed>
181
    */
182 6
    protected function ModifyTypesForDatabase(array $values) : array
183
    {
184
        /**
185
        * @var array<string, mixed>
186
        */
187 6
        $out = array_map(
188
            /**
189
            * @param mixed $val
190
            *
191
            * @return mixed
192
            */
193
            function ($val) {
194 6
                return is_bool($val) ? (int) $val : $val;
195 6
            },
196 6
            $values
197
        );
198
199 6
        return $out;
200
    }
201
202
    abstract protected function DaftObjectDatabaseTable() : string;
203
204
    /**
205
    * @return string[]
206
    */
207 6
    protected function RememberDaftObjectDataCols(DaftObject $object, bool $exists) : array
208
    {
209 6
        $cols = $object::DaftObjectExportableProperties();
210
211 6
        if ($exists) {
212 4
            $changed = $object->ChangedProperties();
213
            $cols = array_filter($cols, function (string $prop) use ($changed) : bool {
214 4
                return in_array($prop, $changed, true);
215 4
            });
216
        }
217
218 6
        return $cols;
219
    }
220
221
    /**
222
    * @return array<string, mixed>
223
    */
224 6
    protected function RememberDaftObjectDataValues(DaftObject $object, bool $exists) : array
225
    {
226
        /**
227
        * @var array<string, mixed>
228
        */
229 6
        $values = [];
230 6
        $cols = $this->RememberDaftObjectDataCols($object, $exists);
231
232 6
        foreach ($cols as $col) {
233
            /**
234
            * @var scalar|null|array|object
235
            */
236 6
            $colVal = $object->$col;
237
238 6
            $values[$col] = $colVal;
239
        }
240
241 6
        return $this->ModifyTypesForDatabase($values);
242
    }
243
244 6
    protected function RememberDaftObjectDataUpdate(bool $exists, array $id, array $values) : void
245
    {
246 6
        if (count($values) > 0) {
247 6
            if (false === $exists) {
248 6
                $this->db->insert($this->DaftObjectDatabaseTable(), $values);
249
            } else {
250 4
                $this->db->update($this->DaftObjectDatabaseTable(), $values, $id);
251
            }
252
        }
253 6
    }
254
255
    /**
256
    * @param mixed $id
257
    */
258 6
    protected function RecallDaftObjectFromData($id) : ? DaftObject
259
    {
260 6
        $idkv = self::DaftObjectIdPropertiesFromType($this->type, $id);
261
262 6
        return $this->RecallDaftObjectFromQuery($idkv);
263
    }
264
265
    /**
266
    * @param array<string, mixed> $idkv
267
    */
268 8
    protected function RecallDaftObjectFromQuery(array $idkv) : ? DefinesOwnIdPropertiesInterface
269
    {
270 8
        $type = $this->type;
271
272 8
        if ( ! is_a($type, DefinesOwnIdPropertiesInterface::class, true)) {
273 2
            throw new InvalidArgumentException(
274
                static::class .
275 2
                '::$type must be an implementation of ' .
276 2
                DefinesOwnIdPropertiesInterface::class .
277 2
                ', ' .
278 2
                $type .
279 2
                ' given!'
280
            );
281
        }
282
283 6
        if (true === $this->DaftObjectExistsInDatabase($idkv)) {
284
            /**
285
            * @var DefinesOwnIdPropertiesInterface
286
            */
287 6
            $out = new $type($this->RecallDaftObjectDataFromQuery($idkv));
288
289 6
            return $out;
290
        }
291
292 4
        return null;
293
    }
294
295 6
    protected function RecallDaftObjectDataFromQuery(array $idkv) : array
296
    {
297
        /**
298
        * @var array[]
299
        */
300 6
        $data = $this->db->safeQuery(
301
            (
302
                'SELECT * FROM ' .
303 6
                $this->db->escapeIdentifier($this->DaftObjectDatabaseTable()) .
304 6
                ' WHERE ' .
305 6
                implode(' AND ', array_map(
306
                    function (string $col) : string {
307 6
                        return $this->db->escapeIdentifier($col) . ' = ?';
308 6
                    },
309 6
                    array_keys($idkv)
310
                )) .
311 6
                ' LIMIT 1'
312
            ),
313 6
            array_values($idkv)
314
        );
315
316 6
        return $data[0];
317
    }
318
319
    /**
320
    * @param array<string, mixed> $id
321
    */
322 6
    protected function DaftObjectExistsInDatabase(array $id) : bool
323
    {
324 6
        $where = [];
325
326 6
        foreach (array_keys($id) as $col) {
327 6
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
328
        }
329
330
        return
331 6
            (int) $this->db->single(
332
                (
333
                    'SELECT COUNT(*) FROM ' .
334 6
                    $this->db->escapeIdentifier($this->DaftObjectDatabaseTable()) .
335 6
                    ' WHERE ' .
336 6
                    implode(' AND ', $where)
337
                ),
338 6
                array_values($id)
339 6
            ) >= 1;
340
    }
341
}
342