1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the miBadger package. |
5
|
|
|
* |
6
|
|
|
* @author Michael Webbers <[email protected]> |
7
|
|
|
* @license http://opensource.org/licenses/Apache-2.0 Apache v2 License |
8
|
|
|
* @version 1.0.0 |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace miBadger\ActiveRecord; |
12
|
|
|
|
13
|
|
|
use miBadger\Query\Query; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* The abstract active record class. |
17
|
|
|
* |
18
|
|
|
* @since 1.0.0 |
19
|
|
|
*/ |
20
|
|
|
abstract class AbstractActiveRecord implements ActiveRecordInterface |
21
|
|
|
{ |
22
|
|
|
/** @var \PDO The PDO object. */ |
23
|
|
|
private $pdo; |
24
|
|
|
|
25
|
|
|
/** @var null|int The ID. */ |
26
|
|
|
private $id; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Construct an abstract active record with the given PDO. |
30
|
|
|
* |
31
|
|
|
* @param \PDO $pdo |
32
|
|
|
*/ |
33
|
28 |
|
public function __construct(\PDO $pdo) |
34
|
|
|
{ |
35
|
28 |
|
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC); |
36
|
28 |
|
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); |
37
|
|
|
|
38
|
28 |
|
$this->setPdo($pdo); |
39
|
28 |
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* {@inheritdoc} |
43
|
|
|
*/ |
44
|
4 |
View Code Duplication |
public function create() |
|
|
|
|
45
|
|
|
{ |
46
|
|
|
try { |
47
|
4 |
|
(new Query($this->getPdo(), $this->getActiveRecordTable())) |
48
|
4 |
|
->insert($this->getActiveRecordColumns()) |
49
|
4 |
|
->execute(); |
50
|
|
|
|
51
|
2 |
|
$this->setId(intval($this->getPdo()->lastInsertId())); |
52
|
4 |
|
} catch (\PDOException $e) { |
53
|
2 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
54
|
|
|
} |
55
|
|
|
|
56
|
2 |
|
return $this; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* {@inheritdoc} |
61
|
|
|
*/ |
62
|
8 |
|
public function read($id) |
63
|
|
|
{ |
64
|
|
|
try { |
65
|
8 |
|
$row = (new Query($this->getPdo(), $this->getActiveRecordTable())) |
66
|
8 |
|
->select() |
67
|
8 |
|
->where('id', '=', $id) |
68
|
8 |
|
->execute() |
69
|
7 |
|
->fetch(); |
70
|
|
|
|
71
|
7 |
|
if ($row === false) { |
72
|
1 |
|
throw new ActiveRecordException(sprintf('Can not read the non-existent active record entry %d from the `%s` table.', $id, $this->getActiveRecordTable())); |
73
|
|
|
} |
74
|
|
|
|
75
|
6 |
|
$this->fill($row)->setId($id); |
76
|
8 |
|
} catch (\PDOException $e) { |
77
|
1 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
78
|
|
|
} |
79
|
|
|
|
80
|
6 |
|
return $this; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* {@inheritdoc} |
85
|
|
|
*/ |
86
|
4 |
View Code Duplication |
public function update() |
|
|
|
|
87
|
|
|
{ |
88
|
|
|
try { |
89
|
4 |
|
(new Query($this->getPdo(), $this->getActiveRecordTable())) |
90
|
4 |
|
->update($this->getActiveRecordColumns()) |
91
|
4 |
|
->where('id', '=', $this->getId()) |
92
|
4 |
|
->execute(); |
93
|
4 |
|
} catch (\PDOException $e) { |
94
|
2 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
95
|
|
|
} |
96
|
|
|
|
97
|
2 |
|
return $this; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* {@inheritdoc} |
102
|
|
|
*/ |
103
|
3 |
View Code Duplication |
public function delete() |
|
|
|
|
104
|
|
|
{ |
105
|
|
|
try { |
106
|
3 |
|
(new Query($this->getPdo(), $this->getActiveRecordTable())) |
107
|
3 |
|
->delete() |
108
|
3 |
|
->where('id', '=', $this->getId()) |
109
|
3 |
|
->execute(); |
110
|
|
|
|
111
|
2 |
|
$this->setId(null); |
112
|
3 |
|
} catch (\PDOException $e) { |
113
|
1 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
114
|
|
|
} |
115
|
|
|
|
116
|
2 |
|
return $this; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* {@inheritdoc} |
121
|
|
|
*/ |
122
|
2 |
|
public function sync() |
123
|
|
|
{ |
124
|
2 |
|
if (!$this->exists()) { |
125
|
1 |
|
return $this->create(); |
126
|
|
|
} |
127
|
|
|
|
128
|
1 |
|
return $this->update(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* {@inheritdoc} |
133
|
|
|
*/ |
134
|
3 |
|
public function exists() |
135
|
|
|
{ |
136
|
3 |
|
return $this->getId() !== null; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* {@inheritdoc} |
141
|
|
|
*/ |
142
|
16 |
|
public function fill(array $attributes) |
143
|
|
|
{ |
144
|
16 |
|
$columns = $this->getActiveRecordColumns(); |
145
|
16 |
|
$columns['id'] = &$this->id; |
146
|
|
|
|
147
|
16 |
|
foreach ($attributes as $key => $value) { |
148
|
16 |
|
if (array_key_exists($key, $columns)) { |
149
|
16 |
|
$columns[$key] = $value; |
150
|
16 |
|
} |
151
|
16 |
|
} |
152
|
|
|
|
153
|
16 |
|
return $this; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* {@inheritdoc} |
158
|
|
|
*/ |
159
|
3 |
|
public function searchOne(array $where = [], array $orderBy = []) |
160
|
|
|
{ |
161
|
|
|
try { |
162
|
3 |
|
$row = $this->getSearchQueryResult($where, $orderBy, 1)->fetch(); |
163
|
|
|
|
164
|
2 |
|
if ($row === false) { |
165
|
1 |
|
throw new ActiveRecordException(sprintf('Can not search one non-existent entry from the `%s` table.', $this->getActiveRecordTable())); |
166
|
|
|
} |
167
|
|
|
|
168
|
1 |
|
return $this->fill($row)->setId($row['id']); |
169
|
2 |
|
} catch (\PDOException $e) { |
170
|
1 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* {@inheritdoc} |
176
|
|
|
*/ |
177
|
27 |
|
public function search(array $where = [], array $orderBy = [], $limit = -1, $offset = 0) |
178
|
|
|
{ |
179
|
|
|
try { |
180
|
27 |
|
$queryResult = $this->getSearchQueryResult($where, $orderBy, $limit, $offset); |
181
|
8 |
|
$result = []; |
182
|
|
|
|
183
|
8 |
|
foreach ($queryResult as $row) { |
184
|
8 |
|
$new = clone $this; |
185
|
|
|
|
186
|
8 |
|
$result[] = $new->fill($row)->setId($row['id']); |
187
|
8 |
|
} |
188
|
|
|
|
189
|
8 |
|
return $result; |
190
|
2 |
|
} catch (\PDOException $e) { |
191
|
2 |
|
throw new ActiveRecordException($e->getMessage(), 0, $e); |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Returns the search query result with the given where, order by, limit and offset clauses. |
197
|
|
|
* |
198
|
|
|
* @param array $where = [] |
199
|
|
|
* @param array $orderBy = [] |
200
|
|
|
* @param int $limit = -1 |
201
|
|
|
* @param int $offset = 0 |
202
|
|
|
* @return \miBadger\Query\QueryResult the search query result with the given where, order by, limit and offset clauses. |
203
|
|
|
*/ |
204
|
13 |
|
private function getSearchQueryResult(array $where = [], array $orderBy = [], $limit = -1, $offset = 0) |
205
|
|
|
{ |
206
|
13 |
|
$query = (new Query($this->getPdo(), $this->getActiveRecordTable())) |
207
|
13 |
|
->select(); |
208
|
|
|
|
209
|
13 |
|
$this->getSearchQueryWhere($query, $where); |
210
|
13 |
|
$this->getSearchQueryOrderBy($query, $orderBy); |
211
|
13 |
|
$this->getSearchQueryLimit($query, $limit, $offset); |
212
|
|
|
|
213
|
13 |
|
return $query->execute(); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* Returns the given query after adding the given where conditions. |
218
|
|
|
* |
219
|
|
|
* @param \miBadger\Query\Query $query |
220
|
|
|
* @param array $where |
221
|
|
|
* @return \miBadger\Query\Query the given query after adding the given where conditions. |
222
|
|
|
*/ |
223
|
13 |
|
private function getSearchQueryWhere($query, $where) |
224
|
|
|
{ |
225
|
13 |
|
foreach ($where as $key => $value) { |
226
|
7 |
|
$query->where($value[0], $value[1], $value[2]); |
227
|
13 |
|
} |
228
|
|
|
|
229
|
13 |
|
return $query; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Returns the given query after adding the given order by conditions. |
234
|
|
|
* |
235
|
|
|
* @param \miBadger\Query\Query $query |
236
|
|
|
* @param array $orderBy |
237
|
|
|
* @return \miBadger\Query\Query the given query after adding the given order by conditions. |
238
|
|
|
*/ |
239
|
13 |
|
private function getSearchQueryOrderBy($query, $orderBy) |
240
|
|
|
{ |
241
|
13 |
|
foreach ($orderBy as $key => $value) { |
242
|
1 |
|
$query->orderBy($key, $value); |
243
|
13 |
|
} |
244
|
|
|
|
245
|
13 |
|
return $query; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Returns the given query after adding the given limit and offset conditions. |
250
|
|
|
* |
251
|
|
|
* @param \miBadger\Query\Query $query |
252
|
|
|
* @param int $limit |
253
|
|
|
* @param int $offset |
254
|
|
|
* @return \miBadger\Query\Query the given query after adding the given limit and offset conditions. |
255
|
|
|
*/ |
256
|
13 |
|
private function getSearchQueryLimit($query, $limit, $offset) |
257
|
|
|
{ |
258
|
13 |
|
if ($limit > -1) { |
259
|
5 |
|
$query->limit($limit); |
260
|
5 |
|
$query->offset($offset); |
261
|
5 |
|
} |
262
|
|
|
|
263
|
13 |
|
return $query; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
/** |
267
|
|
|
* Returns the PDO. |
268
|
|
|
* |
269
|
|
|
* @return \PDO the PDO. |
270
|
|
|
*/ |
271
|
27 |
|
public function getPdo() |
272
|
|
|
{ |
273
|
27 |
|
return $this->pdo; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Set the PDO. |
278
|
|
|
* |
279
|
|
|
* @param \PDO $pdo |
280
|
|
|
* @return $this |
281
|
|
|
*/ |
282
|
28 |
|
protected function setPdo($pdo) |
283
|
|
|
{ |
284
|
28 |
|
$this->pdo = $pdo; |
285
|
|
|
|
286
|
28 |
|
return $this; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Returns the ID. |
291
|
|
|
* |
292
|
|
|
* @return null|int The ID. |
293
|
|
|
*/ |
294
|
7 |
|
public function getId() |
295
|
|
|
{ |
296
|
7 |
|
return $this->id; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Set the ID. |
301
|
|
|
* |
302
|
|
|
* @param int $id |
303
|
|
|
* @return $this |
304
|
|
|
*/ |
305
|
16 |
|
protected function setId($id) |
306
|
|
|
{ |
307
|
16 |
|
$this->id = $id; |
308
|
|
|
|
309
|
16 |
|
return $this; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Returns the active record table. |
314
|
|
|
* |
315
|
|
|
* @return string the active record table. |
316
|
|
|
*/ |
317
|
|
|
abstract protected function getActiveRecordTable(); |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Returns the active record columns. |
321
|
|
|
* |
322
|
|
|
* @return array the active record columns. |
323
|
|
|
*/ |
324
|
|
|
abstract protected function getActiveRecordColumns(); |
325
|
|
|
} |
326
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.