AbstractDaftObjectEasyDBRepository   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 25
eloc 88
dl 0
loc 274
ccs 90
cts 90
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A ModifyTypesForDatabase() 0 25 3
A DaftObjectIdPropertiesFromType() 0 28 5
A DaftObjectRepositoryByType() 0 10 1
A __construct() 0 4 1
A RememberDaftObjectDataUpdate() 0 7 3
A DaftObjectRepositoryByDaftObject() 0 10 1
A RememberDaftObjectDataCols() 0 12 2
A DaftObjectExistsInDatabase() 0 18 2
A RecallDaftObjectFromData() 0 29 2
A RememberDaftObjectData() 0 31 3
A RemoveDaftObjectById() 0 9 2
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 ParagonIE\EasyDB\EasyDB;
12
13
/**
14
* @template TDbObj as SuitableForRepositoryType
15
*
16
* @template-extends DaftObjectMemoryRepository<TDbObj>
17
*/
18
abstract class AbstractDaftObjectEasyDBRepository extends DaftObjectMemoryRepository
19
{
20
    const ARG_SECOND = 2;
21
22
    const BOOL_DOES_NOT_EXIST = false;
23
24
    const BOOL_TRUE_AS_INT = 1;
25
26
    const BOOL_FALSE_AS_INT = 0;
27
28
    const COUNT_EMPTY_ARRAY = 0;
29
30
    /**
31
    * @var EasyDB
32
    */
33
    protected $db;
34
35
    /**
36
    * {@inheritdoc}
37
    *
38
    * @param mixed ...$args
39
    *
40
    * @psalm-param class-string<TDbObj> $type
41
    */
42 8
    protected function __construct(string $type, EasyDB $db, ...$args)
43
    {
44 8
        parent::__construct($type, ...$args);
45 8
        $this->db = $db;
46 8
    }
47
48
    /**
49
    * {@inheritdoc}
50
    *
51
    * @psalm-param class-string<TDbObj> $type
52
    * @psalm-param EasyDB $args[0]
53
    *
54
    * @psalm-return AbstractDaftObjectEasyDBRepository<TDbObj>
55
    */
56 8
    public static function DaftObjectRepositoryByType(
57
        string $type,
58
        ...$args
59
    ) : DaftObjectRepository {
60
        /**
61
        * @psalm-var array{0:EasyDB}
62
        */
63 8
        $args = $args;
64
65 8
        return new static($type, ...$args);
66
    }
67
68
    /**
69
    * {@inheritdoc}
70
    *
71
    * @psalm-param TDbObj $object
72
    * @psalm-param EasyDB $args[0]
73
    *
74
    * @return static
75
    *
76
    * @psalm-return AbstractDaftObjectEasyDBRepository<TDbObj>
77
    */
78 8
    public static function DaftObjectRepositoryByDaftObject(
79
        SuitableForRepositoryType $object,
80
        ...$args
81
    ) : DaftObjectRepository {
82
        /**
83
        * @psalm-var class-string<TDbObj>
84
        */
85 8
        $className = get_class($object);
86
87 8
        return static::DaftObjectRepositoryByType($className, ...$args);
88
    }
89
90
    /**
91
    * @param scalar|(scalar|array|object|null)[] $id
92
    */
93 4
    public function RemoveDaftObjectById($id) : void
94
    {
95 4
        $id = array_values(is_array($id) ? $id : [$id]);
96
97 4
        $idkv = self::DaftObjectIdPropertiesFromType($this->type, $id);
98
99 4
        $this->db->delete($this->DaftObjectDatabaseTable(), $this->ModifyTypesForDatabase($idkv));
100
101 4
        $this->ForgetDaftObjectById($id);
102 4
    }
103
104 8
    public function RememberDaftObjectData(
105
        SuitableForRepositoryType $object,
106
        bool $assumeDoesNotExist = self::BOOL_DOES_NOT_EXIST
107
    ) : void {
108 8
        $id = [];
109
110 8
        foreach ($object::DaftObjectIdProperties() as $prop) {
111 8
            $id[$prop] = $object->__get($prop);
112
        }
113
114
        $this->db->tryFlatTransaction(function () use ($id, $object, $assumeDoesNotExist) : void {
115
            $exists =
116 8
                $assumeDoesNotExist
117 4
                    ? self::BOOL_DOES_NOT_EXIST
118 8
                    : $this->DaftObjectExistsInDatabase($id);
119 8
            $cols = $this->RememberDaftObjectDataCols($object, $exists);
120
121
            /**
122
            * @var array<string, string>
123
            */
124 8
            $cols = array_combine($cols, $cols);
125
126 8
            $this->RememberDaftObjectDataUpdate($exists, $id, $this->ModifyTypesForDatabase(
127 8
                array_map(
128
                    /**
129
                    * @return scalar|array|object|null
130
                    */
131
                    function (string $col) use ($object) {
132 8
                        return $object->__get($col);
133 8
                    },
134 8
                    $cols
0 ignored issues
show
Bug introduced by
It seems like $cols can also be of type false; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

134
                    /** @scrutinizer ignore-type */ $cols
Loading history...
135
                )
136
            ));
137 8
        });
138 8
    }
139
140
    /**
141
    * @return array<string, mixed>
142
    */
143 8
    protected function ModifyTypesForDatabase(array $values) : array
144
    {
145
        /**
146
        * @var array<string, mixed>
147
        */
148 8
        $out = array_map(
149
            /**
150
            * @param mixed $val
151
            *
152
            * @return mixed
153
            */
154
            function ($val) {
155
                return
156 8
                    is_bool($val)
157
                        ? (
158 4
                            $val
159 4
                                ? self::BOOL_TRUE_AS_INT
160 4
                                : self::BOOL_FALSE_AS_INT
161
                        )
162 8
                        : $val;
163 8
            },
164 8
            $values
165
        );
166
167 8
        return $out;
168
    }
169
170
    abstract protected function DaftObjectDatabaseTable() : string;
171
172
    /**
173
    * {@inheritdoc}
174
    */
175 8
    protected function RecallDaftObjectFromData($id) : ? SuitableForRepositoryType
176
    {
177 8
        $idkv = self::DaftObjectIdPropertiesFromType($this->type, $id);
178 8
        $type = $this->type;
179
180 8
        if (true === $this->DaftObjectExistsInDatabase($idkv)) {
181
            /**
182
            * @var array[]
183
            */
184 8
            $data = $this->db->safeQuery(
185
                (
186
                    'SELECT * FROM ' .
187 8
                    $this->db->escapeIdentifier($this->DaftObjectDatabaseTable()) .
188 8
                    ' WHERE ' .
189 8
                    implode(' AND ', array_map(
190
                        function (string $col) : string {
191 8
                            return $this->db->escapeIdentifier($col) . ' = ?';
192 8
                        },
193 8
                        array_keys($idkv)
194
                    )) .
195 8
                    ' LIMIT 1'
196
                ),
197 8
                array_values($idkv)
198
            );
199
200 8
            return new $type($data[0]);
201
        }
202
203 4
        return null;
204
    }
205
206
    /**
207
    * @param mixed $id
208
    *
209
    * @psalm-param class-string<TDbObj> $type
210
    *
211
    * @return array<string, mixed>
212
    */
213 8
    private static function DaftObjectIdPropertiesFromType(string $type, $id) : array
214
    {
215
        /**
216
        * @var array<int, string>
217
        */
218 8
        $idProps = array_values($type::DaftObjectIdProperties());
219
220 8
        if (is_scalar($id) && 1 === count($idProps)) {
221 8
            $id = [$id];
222
        }
223
224
        /**
225
        * @var array<string, mixed>
226
        */
227 8
        $idkv = [];
228
229 8
        if (is_array($id)) {
230 8
            foreach ($idProps as $i => $prop) {
231
                /**
232
                * @var scalar|array|object|null
233
                */
234 8
                $propVal = $id[$i] ?? null;
235
236 8
                $idkv[$prop] = $propVal;
237
            }
238
        }
239
240 8
        return $idkv;
241
    }
242
243
    /**
244
    * @return string[]
245
    */
246 8
    private function RememberDaftObjectDataCols(DaftObject $object, bool $exists) : array
247
    {
248 8
        $cols = $object::DaftObjectExportableProperties();
249
250 8
        if ($exists) {
251 8
            $changed = $object->ChangedProperties();
252
            $cols = array_filter($cols, function (string $prop) use ($changed) : bool {
253 8
                return in_array($prop, $changed, DefinitionAssistant::IN_ARRAY_STRICT_MODE);
254 8
            });
255
        }
256
257 8
        return $cols;
258
    }
259
260 8
    private function RememberDaftObjectDataUpdate(bool $exists, array $id, array $values) : void
261
    {
262 8
        if (count($values) > self::COUNT_EMPTY_ARRAY) {
263 8
            if (false === $exists) {
264 8
                $this->db->insert($this->DaftObjectDatabaseTable(), $values);
265
            } else {
266 4
                $this->db->update($this->DaftObjectDatabaseTable(), $values, $id);
267
            }
268
        }
269 8
    }
270
271
    /**
272
    * @param array<string, mixed> $id
273
    */
274 8
    private function DaftObjectExistsInDatabase(array $id) : bool
275
    {
276 8
        $where = [];
277
278 8
        foreach (array_keys($id) as $col) {
279 8
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
280
        }
281
282
        return
283 8
            $this->db->single(
284
                (
285
                    'SELECT COUNT(*) FROM ' .
286 8
                    $this->db->escapeIdentifier($this->DaftObjectDatabaseTable()) .
287 8
                    ' WHERE ' .
288 8
                    implode(' AND ', $where)
289
                ),
290 8
                array_values($id)
291 8
            ) >= 1;
292
    }
293
}
294