Completed
Push — master ( 8bf117...951fd4 )
by SignpostMarv
03:41
created

RemoveDaftObjectById()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 4
nop 1
dl 0
loc 33
ccs 15
cts 15
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
            false === is_a(
34 6
                $type,
35 6
                DefinesOwnIdPropertiesInterface::class,
36 6
                true
37
            )
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 (false === ($db instanceof EasyDB)) {
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 (true === ($db instanceof EasyDB)) {
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
80
        /**
81
        * @var DefinesOwnIdPropertiesInterface $type
82
        */
83 2
        $type = $this->type;
84 2
        $idkv = [];
85
86
        foreach (
87 2
            array_values($type::DaftObjectIdProperties()) as $i => $prop
88
        ) {
89 2
            $idkv[$prop] = $id[$i];
90
        }
91
92 2
        $where = [];
93 2
        foreach (array_keys($idkv) as $col) {
94 2
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
95
        }
96
97
        $query = (
98
            'DELETE FROM ' .
99 2
            $this->db->escapeIdentifier(
100 2
                $this->DaftObjectDatabaseTable()
101
            ) .
102 2
            ' WHERE ' .
103 2
            implode(' AND ', $where)
104
        );
105
106 2
        $this->db->safeQuery($query, array_values($idkv));
107
108 2
        $this->ForgetDaftObjectById($id);
109 2
    }
110
111
    abstract protected function DaftObjectDatabaseTable() : string;
112
113 2
    protected function RememberDaftObjectData(
114
        DefinesOwnIdPropertiesInterface $object
115
    ) : void {
116 2
        $id = [];
117
118 2
        foreach ($object::DaftObjectIdProperties() as $prop) {
119 2
            $id[$prop] = $object->$prop;
120
        }
121
122 2
        $autoStartTransaction = (false === $this->db->inTransaction());
123
124 2
        if (true === $autoStartTransaction) {
125 2
            $this->db->beginTransaction();
126
        }
127
128
        try {
129 2
            $exists = $this->DaftObjectExistsInDatabase($id);
130 2
            if (false === $exists) {
131 2
                $cols = [];
132 2
                $values = [];
133
134 2
                foreach ($object::DaftObjectProperties() as $col) {
135
                    if (
136 2
                        false === method_exists(
137 2
                            $object,
138 2
                            'Get' . ucfirst($col)
139
                        )
140
                    ) {
141 2
                        continue;
142
                    }
143 2
                    $cols[] = $this->db->escapeIdentifier($col);
144 2
                    $values[] = $object->$col;
145
                }
146
147 2
                $this->db->safeQuery(
148
                    (
149
                        'INSERT INTO ' .
150 2
                        $this->db->escapeIdentifier(
151 2
                            $this->DaftObjectDatabaseTable()
152
                        ) .
153 2
                        ' (' .
154 2
                        implode(', ', $cols) .
155 2
                        ') VALUES (' .
156 2
                        implode(', ', array_fill(0, count($cols), '?')) .
157 2
                        ')'
158
                    ),
159 2
                    $values
160
                );
161
            } else {
162 2
                $changed = $object->ChangedProperties();
163 2
                if (count($changed) > 0) {
164 2
                    $cols = [];
165 2
                    $values = [];
166
167 2
                    foreach ($changed as $col) {
168 2
                        $values[] = $object->$col;
169 2
                        $cols[] =
170 2
                            $this->db->escapeIdentifier($col) .
171 2
                            ' = ?';
172
                    }
173
174
                    $query =
175
                        'UPDATE ' .
176 2
                        $this->db->escapeIdentifier(
177 2
                            $this->DaftObjectDatabaseTable()
178
                        ) .
179 2
                        ' SET ' .
180 2
                        implode(', ', $cols);
181
182 2
                    $cols = [];
183
184 2
                    foreach ($id as $col => $value) {
185 2
                        $values[] = $value;
186 2
                        $cols[] =
187 2
                            $this->db->escapeIdentifier($col) .
188 2
                            ' = ?';
189
                    }
190
191 2
                    $query .= ' WHERE ' . implode(' AND ', $cols);
192
193 2
                    $this->db->safeQuery($query, $values);
194
                }
195
            }
196
197 2
            if (true === $autoStartTransaction) {
198 2
                $this->db->commit();
199
            }
200
        } catch (Throwable $e) {
201
            if (true === $autoStartTransaction) {
202
                $this->db->rollBack();
203
            }
204
205
            throw $e;
206
        }
207 2
    }
208
209
    /**
210
    * @param mixed $id
211
    */
212 2
    protected function RecallDaftObjectFromData($id) : ? DaftObject
213
    {
214
        /**
215
        * @var DefinesOwnIdPropertiesInterface $type
216
        */
217 2
        $type = $this->type;
218 2
        $idkv = [];
219
220
        foreach (
221 2
            array_values($type::DaftObjectIdProperties()) as $i => $prop
222
        ) {
223 2
            $idkv[$prop] = $id[$i];
224
        }
225
226 2
        if (true === $this->DaftObjectExistsInDatabase($idkv)) {
227 2
            $where = [];
228 2
            foreach (array_keys($idkv) as $col) {
229 2
                $where[] = $this->db->escapeIdentifier($col) . ' = ?';
230
            }
231
232 2
            $data = $this->db->safeQuery(
233
                (
234
                    'SELECT * FROM ' .
235 2
                    $this->db->escapeIdentifier(
236 2
                        $this->DaftObjectDatabaseTable()
237
                    ) .
238 2
                    ' WHERE ' .
239 2
                    implode(' AND ', $where) .
240 2
                    ' LIMIT 1'
241
                ),
242 2
                array_values($idkv)
243
            );
244
245 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...
Unused Code introduced by
The call to SignpostMarv\DaftObject\...nterface::__construct() has too many arguments starting with $data[0]. ( Ignorable by Annotation )

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

245
            return /** @scrutinizer ignore-call */ new $type($data[0]);

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...
246
        }
247
248 2
        return null;
249
    }
250
251 2
    private function DaftObjectExistsInDatabase(array $id) : bool
252
    {
253 2
        $where = [];
254 2
        foreach (array_keys($id) as $col) {
255 2
            $where[] = $this->db->escapeIdentifier($col) . ' = ?';
256
        }
257
258
        return
259 2
            (int) $this->db->single(
260
                (
261
                    'SELECT COUNT(*) FROM ' .
262 2
                    $this->db->escapeIdentifier(
263 2
                        $this->DaftObjectDatabaseTable()
264
                    ) .
265 2
                    ' WHERE ' .
266 2
                    implode(' AND ', $where)
267
                ),
268 2
                array_values($id)
269 2
            ) >= 1;
270
    }
271
}
272