Passed
Push — master ( b43514...976808 )
by SignpostMarv
02:45
created

RememberDaftObjectDataUpdate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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