1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Koine\Repository\Storage\Pdo; |
4
|
|
|
|
5
|
|
|
use Koine\Repository\Exception\RecordNotFoundException; |
6
|
|
|
use Koine\Repository\Storage\StorageInterface; |
7
|
|
|
use PDO; |
8
|
|
|
|
9
|
|
|
abstract class AbstractStorage implements StorageInterface |
10
|
|
|
{ |
11
|
|
|
/** |
12
|
|
|
* @var PDO |
13
|
|
|
*/ |
14
|
|
|
private $connection; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var string |
18
|
|
|
*/ |
19
|
|
|
private $tableName; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var string |
23
|
|
|
*/ |
24
|
|
|
private $idColumn; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @param PDO $connection |
28
|
|
|
* @param string $tableName |
29
|
|
|
* @param string $idColumn |
30
|
|
|
*/ |
31
|
|
|
public function __construct(PDO $connection, $tableName, $idColumn = 'id') |
32
|
|
|
{ |
33
|
|
|
$this->setConnection($connection); |
34
|
|
|
$this->setTableName($tableName); |
35
|
|
|
$this->setIdColumn($idColumn); |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @param PDO $connection |
40
|
|
|
* |
41
|
|
|
* @return self |
42
|
|
|
*/ |
43
|
|
|
public function setConnection(PDO $connection) |
44
|
|
|
{ |
45
|
|
|
$this->connection = $connection; |
46
|
|
|
return $this; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @return PDO |
51
|
|
|
*/ |
52
|
|
|
public function getConnection() |
53
|
|
|
{ |
54
|
|
|
return $this->connection; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @param string $tableName |
59
|
|
|
* |
60
|
|
|
* @return self |
61
|
|
|
*/ |
62
|
|
|
public function setTableName($tableName) |
63
|
|
|
{ |
64
|
|
|
$this->tableName = (string) $tableName; |
65
|
|
|
return $this; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @return string |
70
|
|
|
*/ |
71
|
|
|
public function getTableName() |
72
|
|
|
{ |
73
|
|
|
return $this->tableName; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @param string $idColumn |
78
|
|
|
* |
79
|
|
|
* @return self |
80
|
|
|
*/ |
81
|
|
|
public function setIdColumn($idColumn) |
82
|
|
|
{ |
83
|
|
|
$this->idColumn = (string) $idColumn; |
84
|
|
|
return $this; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @return string |
89
|
|
|
*/ |
90
|
|
|
public function getIdColumn() |
91
|
|
|
{ |
92
|
|
|
return $this->idColumn; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @return int |
97
|
|
|
*/ |
98
|
|
|
public function getNumberOfRows() |
99
|
|
|
{ |
100
|
|
|
$tableName = $this->getTableName(); |
101
|
|
|
$records = $this->selectQuery("SELECT count(*) as count FROM $tableName"); |
102
|
|
|
|
103
|
|
|
return (int) $records[0]['count']; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param string $sql |
108
|
|
|
* @param array $params |
109
|
|
|
* |
110
|
|
|
* @return array |
111
|
|
|
*/ |
112
|
|
|
private function selectQuery($sql, array $params = array()) |
113
|
|
|
{ |
114
|
|
|
$stmt = $this->getConnection()->prepare($sql); |
115
|
|
|
$stmt->execute($params); |
116
|
|
|
|
117
|
|
|
return $stmt->fetchAll(PDO::FETCH_ASSOC); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @param array $values |
122
|
|
|
*/ |
123
|
|
|
public function insert(array $values) |
124
|
|
|
{ |
125
|
|
|
$sql = 'INSERT INTO %s (%s) VALUES (%s)'; |
126
|
|
|
$columnNames = array_keys($values); |
127
|
|
|
$columns = implode(', ', $columnNames); |
128
|
|
|
$placeholders = implode(', ', $this->columnsToPlaceholders($columnNames)); |
129
|
|
|
$sql = sprintf( |
130
|
|
|
$sql, |
131
|
|
|
$this->getTableName(), |
132
|
|
|
$columns, |
133
|
|
|
$placeholders |
134
|
|
|
); |
135
|
|
|
$stmt = $this->getConnection()->prepare($sql); |
136
|
|
|
$stmt->execute($values); |
137
|
|
|
|
138
|
|
|
return $this->getConnection()->lastInsertId(); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @param array $columns |
143
|
|
|
* @param string $placeholderPrefix |
144
|
|
|
* |
145
|
|
|
* @return array |
146
|
|
|
*/ |
147
|
|
|
private function columnsToPlaceholders(array $columns, $placeholderPrefix = '') |
148
|
|
|
{ |
149
|
|
|
$placeholders = array(); |
150
|
|
|
|
151
|
|
|
foreach ($columns as $column) { |
152
|
|
|
$placeholders[$column] = ":$placeholderPrefix$column"; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
return $placeholders; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
public function find($id) |
159
|
|
|
{ |
160
|
|
|
$resultSet = $this->findAllBy(array( |
161
|
|
|
$this->getIdColumn() => $id, |
162
|
|
|
)); |
163
|
|
|
|
164
|
|
|
if (count($resultSet)) { |
165
|
|
|
return $resultSet[0]; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
throw new RecordNotFoundException( |
169
|
|
|
sprintf( |
170
|
|
|
'%s record not found by %s %s', |
171
|
|
|
$this->getTableName(), |
172
|
|
|
$this->getIdColumn(), |
173
|
|
|
$id |
174
|
|
|
) |
175
|
|
|
); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
public function findAllBy(array $conditions = array(), $limit = null, $offset = 0) |
179
|
|
|
{ |
180
|
|
|
$sql = sprintf('SELECT * FROM %s', $this->getTableName()); |
181
|
|
|
|
182
|
|
|
if ($conditions) { |
|
|
|
|
183
|
|
|
$whereConditions = $this->assembleEquality($conditions); |
184
|
|
|
$where = sprintf('WHERE %s', implode(' AND ', $whereConditions)); |
185
|
|
|
$sql .= " $where"; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
if ($limit) { |
189
|
|
|
$sql .= sprintf(' LIMIT %s, %s', (int) $offset, (int) $limit); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return $this->selectQuery($sql, $conditions); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
private function assembleEquality(array $conditions, $placeholderPrefix = '') |
196
|
|
|
{ |
197
|
|
|
$placeholders = $this->columnsToPlaceholders(array_keys($conditions), $placeholderPrefix); |
198
|
|
|
$equalities = array(); |
199
|
|
|
|
200
|
|
|
foreach ($placeholders as $column => $placeholder) { |
201
|
|
|
$equalities[] = "$column = $placeholder"; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return $equalities; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @param array $conditions |
209
|
|
|
* @param array $values |
210
|
|
|
*/ |
211
|
|
|
public function updateWhere(array $conditions, array $values) |
212
|
|
|
{ |
213
|
|
|
$conditionsParams = array(); |
214
|
|
|
|
215
|
|
|
foreach ($conditions as $column => $value) { |
216
|
|
|
$conditionsParams["_$column"] = $value; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
$conditions = $this->assembleEquality($conditions, '_'); |
220
|
|
|
$conditionsString = implode(' AND ', $conditions); |
221
|
|
|
|
222
|
|
|
$modifications = $this->assembleEquality($values); |
223
|
|
|
$modificationsString = implode(', ', $modifications); |
224
|
|
|
|
225
|
|
|
$sql = sprintf( |
226
|
|
|
'UPDATE %s SET %s WHERE %s', |
227
|
|
|
$this->getTableName(), |
228
|
|
|
$modificationsString, |
229
|
|
|
$conditionsString |
230
|
|
|
); |
231
|
|
|
|
232
|
|
|
$params = array_merge($conditionsParams, $values); |
233
|
|
|
$stmt = $this->getConnection()->prepare($sql); |
234
|
|
|
$stmt->execute($params); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function findOneBy(array $conditions) |
238
|
|
|
{ |
239
|
|
|
$result = $this->findAllBy($conditions, 1); |
240
|
|
|
|
241
|
|
|
if ($result) { |
|
|
|
|
242
|
|
|
return $result[0]; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
throw new RecordNotFoundException('Record not found'); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
public function exists(array $conditions) |
249
|
|
|
{ |
250
|
|
|
$resultSet = $this->findAllBy($conditions, 1); |
251
|
|
|
return count($resultSet) !== 0; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* @param array $conditions |
256
|
|
|
*/ |
257
|
|
|
public function deleteWhere(array $conditions) |
258
|
|
|
{ |
259
|
|
|
$equalities = $this->assembleEquality($conditions); |
260
|
|
|
$conditionsString = implode(' AND ', $equalities); |
261
|
|
|
|
262
|
|
|
$sql = sprintf( |
263
|
|
|
'DELETE FROM %s WHERE %s', |
264
|
|
|
$this->getTableName(), |
265
|
|
|
$conditionsString |
266
|
|
|
); |
267
|
|
|
|
268
|
|
|
$stmt = $this->getConnection()->prepare($sql); |
269
|
|
|
$stmt->execute($conditions); |
270
|
|
|
} |
271
|
|
|
} |
272
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.