Passed
Push — master ( 86fb92...8bf117 )
by SignpostMarv
02:04
created

RecallDaftObjectFromData()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 6
nop 1
dl 0
loc 34
ccs 18
cts 18
cp 1
crap 4
rs 8.5806
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 ParagonIE\EasyDB\EasyDB;
12
use RuntimeException;
13
use Throwable;
14
15
abstract class AbstractDaftObjectEasyDBRepository extends DaftObjectMemoryRepository
16
{
17
    /**
18
    * @var EasyDB
19
    */
20
    protected $db;
21
22 6
    protected function __construct(string $type, EasyDB $db)
23
    {
24 6
        parent::__construct($type);
25 6
        $this->db = $db;
26 6
    }
27
28 6
    public static function DaftObjectRepositoryByType(
29
        string $type,
30
        ? EasyDB $db = null
31
    ) : DaftObjectRepository {
32
        if (
33 6
            is_a(
34 6
                $type,
0 ignored issues
show
Bug introduced by
$type of type string is incompatible with the type object expected by parameter $object of is_a(). ( Ignorable by Annotation )

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

34
                /** @scrutinizer ignore-type */ $type,
Loading history...
35 6
                DefinesOwnIdPropertiesInterface::class,
36 6
                true
37 6
            ) === false
38
        ) {
39 1
            throw new DaftObjectRepositoryTypeException(
40
                'Argument 1 passed to ' .
0 ignored issues
show
Unused Code introduced by
The call to SignpostMarv\DaftObject\...xception::__construct() has too many arguments starting with 'Argument 1 passed to ' ..., ' . $type . ' given.'. ( Ignorable by Annotation )

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

40
            throw /** @scrutinizer ignore-call */ new DaftObjectRepositoryTypeException(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
41 1
                static::class .
42
                '::' .
43
                __FUNCTION__ .
44
                '() must be an implementation of ' .
45
                DefinesOwnIdPropertiesInterface::class .
46 1
                ', ' .
47 1
                $type .
48 1
                ' given.'
49
            );
50 6
        } elseif (($db instanceof EasyDB) === false) {
51
            throw new RuntimeException('Database connection not specified!');
52
        }
53
54
        /**
55
        * @var EasyDB $db
56
        */
57 6
        $db = $db;
58
59 6
        return new static($type, $db);
60
    }
61
62 5
    public static function DaftObjectRepositoryByDaftObject(
63
        DefinesOwnIdPropertiesInterface $object,
64
        ? EasyDB $db = null
65
    ) : DaftObjectRepository {
66 5
        if (($db instanceof EasyDB) === true) {
67 5
            return static::DaftObjectRepositoryByType(get_class($object), $db);
68
        }
69
70
        throw new RuntimeException('Database connection not specified!');
71
    }
72
73
    /**
74
    * @param mixed $id
75
    */
76 2
    public function RemoveDaftObjectById($id) : void
77
    {
78 2
        $id = array_values(is_array($id) ? $id : [$id]);
79 2
        $type = $this->type;
80 2
        $idkv = [];
81
82
        foreach (
83 2
            array_values($type::DaftObjectIdProperties()) as $i => $prop
84
        ) {
85 2
            $idkv[$prop] = $id[$i];
86
        }
87
88 2
        $where = [];
89 2
        foreach (array_keys($idkv) as $col) {
90 2
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
91
        }
92
93
        $query = (
94
            'DELETE FROM ' .
95 2
            $this->db->escapeIdentifier(
96 2
                $this->DaftObjectDatabaseTable()
97
            ) .
98 2
            ' WHERE ' .
99 2
            implode(' AND ', $where)
100
        );
101
102 2
        $this->db->safeQuery($query, array_values($idkv));
103
104 2
        $this->ForgetDaftObjectById($id);
105 2
    }
106
107
    abstract protected function DaftObjectDatabaseTable() : string;
108
109 2
    protected function RememberDaftObjectData(
110
        DefinesOwnIdPropertiesInterface $object
111
    ) : void {
112 2
        $id = [];
113
114 2
        foreach ($object::DaftObjectIdProperties() as $prop) {
115 2
            $id[$prop] = $object->$prop;
116
        }
117
118 2
        $autoStartTransaction = ($this->db->inTransaction() === false);
119
120 2
        if ($autoStartTransaction === true) {
121 2
            $this->db->beginTransaction();
122
        }
123
124
        try {
125 2
            $exists = $this->DaftObjectExistsInDatabase($id);
126 2
            if ($exists === false) {
127 2
                $cols = [];
128 2
                $values = [];
129
130 2
                foreach ($object::DaftObjectProperties() as $col) {
131
                    if (
132 2
                        method_exists(
133 2
                            $object,
134 2
                            'Get' . ucfirst($col)
135 2
                        ) === false
136
                    ) {
137 2
                        continue;
138
                    }
139 2
                    $cols[] = $this->db->escapeIdentifier($col);
140 2
                    $values[] = $object->$col;
141
                }
142
143 2
                $this->db->safeQuery(
144
                    (
145
                        'INSERT INTO ' .
146 2
                        $this->db->escapeIdentifier(
147 2
                            $this->DaftObjectDatabaseTable()
148
                        ) .
149 2
                        ' (' .
150 2
                        implode(', ', $cols) .
151 2
                        ') VALUES (' .
152 2
                        implode(', ', array_fill(0, count($cols), '?')) .
153 2
                        ')'
154
                    ),
155 2
                    $values
156
                );
157
            } else {
158 2
                $changed = $object->ChangedProperties();
159 2
                if (count($changed) > 0) {
160 2
                    $cols = [];
161 2
                    $values = [];
162
163 2
                    foreach ($changed as $col) {
164 2
                        $values[] = $object->$col;
165 2
                        $cols[] =
166 2
                            $this->db->escapeIdentifier($col) .
167 2
                            ' = ?';
168
                    }
169
170
                    $query =
171
                        'UPDATE ' .
172 2
                        $this->db->escapeIdentifier(
173 2
                            $this->DaftObjectDatabaseTable()
174
                        ) .
175 2
                        ' SET ' .
176 2
                        implode(', ', $cols);
177
178 2
                    $cols = [];
179
180 2
                    foreach ($id as $col => $value) {
181 2
                        $values[] = $value;
182 2
                        $cols[] =
183 2
                            $this->db->escapeIdentifier($col) .
184 2
                            ' = ?';
185
                    }
186
187 2
                    $query .= ' WHERE ' . implode(' AND ', $cols);
188
189 2
                    $this->db->safeQuery($query, $values);
190
                }
191
            }
192
193 2
            if ($autoStartTransaction === true) {
194 2
                $this->db->commit();
195
            }
196
        } catch (Throwable $e) {
197
            if ($autoStartTransaction === true) {
198
                $this->db->rollBack();
199
            }
200
201
            throw $e;
202
        }
203 2
    }
204
205
    /**
206
    * @param mixed $id
207
    */
208 2
    protected function RecallDaftObjectFromData($id) : ? DaftObject
209
    {
210 2
        $type = $this->type;
211 2
        $idkv = [];
212
213
        foreach (
214 2
            array_values($type::DaftObjectIdProperties()) as $i => $prop
215
        ) {
216 2
            $idkv[$prop] = $id[$i];
217
        }
218
219 2
        if ($this->DaftObjectExistsInDatabase($idkv) === true) {
220 2
            $where = [];
221 2
            foreach (array_keys($idkv) as $col) {
222 2
                $where[] = $this->db->escapeIdentifier($col) . ' = ?';
223
            }
224
225 2
            $data = $this->db->safeQuery(
226
                (
227
                    'SELECT * FROM ' .
228 2
                    $this->db->escapeIdentifier(
229 2
                        $this->DaftObjectDatabaseTable()
230
                    ) .
231 2
                    ' WHERE ' .
232 2
                    implode(' AND ', $where) .
233 2
                    ' LIMIT 1'
234
                ),
235 2
                array_values($idkv)
236
            );
237
238 2
            return new $type($data[0]);
0 ignored issues
show
Bug Best Practice introduced by
The expression return new $type($data[0]) returns the type object which includes types incompatible with the type-hinted return null|SignpostMarv\DaftObject\DaftObject.
Loading history...
239
        }
240
241 2
        return null;
242
    }
243
244 2
    private function DaftObjectExistsInDatabase(array $id) : bool
245
    {
246 2
        $where = [];
247 2
        foreach (array_keys($id) as $col) {
248 2
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
249
        }
250
251
        return
252 2
            (int) $this->db->single(
253
                (
254
                    'SELECT COUNT(*) FROM ' .
255 2
                    $this->db->escapeIdentifier(
256 2
                        $this->DaftObjectDatabaseTable()
257
                    ) .
258 2
                    ' WHERE ' .
259 2
                    implode(' AND ', $where)
260
                ),
261 2
                array_values($id)
262 2
            ) >= 1;
263
    }
264
}
265