Completed
Push — develop ( e897dd...9c27c4 )
by Michael
04:54
created

AbstractActiveRecord::getCreateQuery()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 11
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 0
crap 2
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
/**
14
 * The abstract active record class.
15
 *
16
 * @since 1.0.0
17
 */
18
abstract class AbstractActiveRecord implements ActiveRecordInterface
19
{
20
	/** @var \PDO The PDO object. */
21
	private $pdo;
22
23
	/** @var null|int The ID. */
24
	private $id;
25
26
	/**
27
	 * Construct an abstract pdo active record with the given pdo.
28
	 *
29
	 * @param \PDO $pdo
30
	 */
31 26
	public function __construct(\PDO $pdo)
32
	{
33 26
		$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
34 26
		$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
35
36 26
		$this->setPdo($pdo);
37 26
	}
38
39
	/**
40
	 * {@inheritdoc}
41
	 */
42 3
	public function create()
43
	{
44
		try {
45 3
			$pdoStatement = $this->getPdo()->prepare($this->getCreateQuery());
46 1
			$pdoStatement->execute($this->getActiveRecordData());
47
48 1
			$this->setId(intval($this->getPdo()->lastInsertId()));
49 3
		} catch (\PDOException $e) {
50 2
			throw new ActiveRecordException(sprintf('Can not create a new active record entry in the `%s` table.', $this->getActiveRecordName()), 0, $e);
51
		}
52
53 1
		return $this;
54
	}
55
56
	/**
57
	 * Returns the create query.
58
	 *
59
	 * @return string the create query.
60
	 */
61 3 View Code Duplication
	private function getCreateQuery()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
62
	{
63 3
		$columns = array_keys($this->getActiveRecordData());
64 3
		$values = [];
65
66 3
		foreach ($columns as $key => $value) {
67 3
			$values[] = sprintf(':%s', $value);
68 3
		}
69
70 3
		return sprintf('INSERT INTO `%s` (`%s`) VALUES (%s)', $this->getActiveRecordName(), implode('`, `', $columns), implode(', ', $values));
71
	}
72
73
	/**
74
	 * {@inheritdoc}
75
	 */
76 7
	public function read($id)
77
	{
78
		try {
79 7
			$pdoStatement = $this->getPdo()->prepare($this->getReadQuery());
80 6
			$pdoStatement->execute(['id' => $id]);
81 6
			$result = $pdoStatement->fetch();
82
83 6
			if ($result === false) {
84 1
				throw new ActiveRecordException(sprintf('Can not read the non-existent active record entry %d from the `%s` table.', $id, $this->getActiveRecordName()));
85
			}
86
87 5
			$this->setActiveRecordData($result);
88 4
			$this->setId($id);
89 7
		} catch (\PDOException $e) {
90 1
			throw new ActiveRecordException(sprintf('Can not read active record entry %d from the `%s` table.', $id, $this->getActiveRecordName()), 0, $e);
91
		}
92
93 4
		return $this;
94
	}
95
96
	/**
97
	 * Returns the read query.
98
	 *
99
	 * @return string the read query.
100
	 */
101 7
	private function getReadQuery()
102
	{
103 7
		return sprintf('SELECT * FROM `%s` WHERE `id` = :id', $this->getActiveRecordName());
104
	}
105
106
	/**
107
	 * {@inheritdoc}
108
	 */
109 4 View Code Duplication
	public function update()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
110
	{
111 4
		if (!$this->exists()) {
112 1
			throw new ActiveRecordException(sprintf('Can not update a non-existent active record entry to the `%s` table.', $this->getActiveRecordName()));
113
		}
114
115
		try {
116 3
			$pdoStatement = $this->getPdo()->prepare($this->getUpdateQuery());
117 1
			$pdoStatement->execute(['id' => $this->getId()] + $this->getActiveRecordData());
118 3
		} catch (\PDOException $e) {
119 2
			throw new ActiveRecordException(sprintf('Can not update active record entry %d to the `%s` table.', $this->getId(), $this->getActiveRecordName()), 0, $e);
120
		}
121
122 1
		return $this;
123
	}
124
125
	/**
126
	 * Returns the update query.
127
	 *
128
	 * @return string the update query.
129
	 */
130 3 View Code Duplication
	private function getUpdateQuery()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
131
	{
132 3
		$values = [];
133
134 3
		foreach (array_keys($this->getActiveRecordData()) as $key => $value) {
135 3
			$values[] = sprintf('`%s` = :%s', $value, $value);
136 3
		}
137
138 3
		return sprintf('UPDATE `%s` SET %s WHERE `id` = :id', $this->getActiveRecordName(), implode(', ', $values));
139
	}
140
141
	/**
142
	 * {@inheritdoc}
143
	 */
144 3 View Code Duplication
	public function delete()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
145
	{
146 3
		if (!$this->exists()) {
147 1
			throw new ActiveRecordException(sprintf('Can not delete a non-existent active record entry from the `%s` table.', $this->getActiveRecordName()));
148
		}
149
150
		try {
151 2
			$pdoStatement = $this->getPdo()->prepare($this->getDeleteQuery());
152 1
			$pdoStatement->execute(['id' => $this->getId()]);
153
154 1
			$this->setId(null);
155 2
		} catch (\PDOException $e) {
156 1
			throw new ActiveRecordException(sprintf('Can not delete active record entry %d from the `%s` table.', $this->getId(), $this->getActiveRecordName()), 0, $e);
157
		}
158
159 1
		return $this;
160
	}
161
162
	/**
163
	 * Returns the delete query.
164
	 *
165
	 * @return string the delete query.
166
	 */
167 2
	private function getDeleteQuery()
168
	{
169 2
		return sprintf('DELETE FROM `%s` WHERE `id` = :id', $this->getActiveRecordName());
170
	}
171
172
	/**
173
	 * {@inheritdoc}
174
	 */
175 8
	public function exists()
176
	{
177 8
		return $this->getId() !== null;
178
	}
179
180
	/**
181
	 * {@inheritdoc}
182
	 */
183
	public function search($where = [], $orderBy = [], $limit = -1, $offset = 0)
184
	{
185 24
		try {
186
			$pdoStatement = $this->getPdo()->prepare($this->getSearchQuery($where, $orderBy, $limit, $offset));
187 24
			array_walk_recursive($where, function(&$value) use ($pdoStatement) {
188
				static $index = 1;
189
190
				$pdoStatement->bindParam($index++, $value);
191
			});
192
193
			$pdoStatement->execute();
194
			$result = [];
195
196 26
			while ($fetch = $pdoStatement->fetch()) {
197
				$new = new static($this->getPdo());
198 26
199
				$new->setId(intval($fetch['id']));
200 26
				$new->setActiveRecordData($fetch);
201
202
				$result[] = $new;
203
			}
204
205
			return $result;
206
		} catch (\PDOException $e) {
207
			throw new ActiveRecordException(sprintf('Can not search the record in the `%s` table.', $this->getActiveRecordName()), 0, $e);
208 6
		}
209
	}
210 6
211
	/**
212
	 * Returns the search query with the given where, order by, limit and offset clauses.
213
	 *
214
	 * @param array $where = []
215
	 * @param array $orderBy = []
216
	 * @param int $limit = -1
217
	 * @param int $offset = 0
218
	 * @return string the search query with the given where, order by, limit and offset clauses.
219 13
	 */
220
	private function getSearchQuery($where = [], $orderBy = [], $limit = -1, $offset = 0)
221 13
	{
222
		return sprintf(
223 13
			'SELECT * FROM `%s` %s %s %s',
224
			$this->getActiveRecordName(),
225
			$this->getSearchQueryWhereClause($where),
226
			$this->getSearchQueryOrderByClause($orderBy),
227
			$this->getSearchQueryLimitClause($limit, $offset)
228
		);
229
	}
230
231
	/**
232
	 * Returns the search query where clause.
233
	 *
234
	 * @param array $where
235
	 * @return string the search query where clause.
236
	 */
237
	private function getSearchQueryWhereClause($where)
238
	{
239
		$columns = array_keys($this->getActiveRecordData());
240
		$columns[] = 'id';
241
		$result = [];
242
243
		foreach ($where as $key => $value) {
244
			if (!in_array($key, $columns)) {
245
				throw new ActiveRecordException(sprintf('Search option key `%s` does not exists.', $key));
246 13
			}
247
248 13
			if (is_numeric($value)) {
249
				$result[] = sprintf('`%s` = ?', $key);
250 13
			} elseif (is_string($value)) {
251 13
				$result[] = sprintf('`%s` LIKE ?', $key);
252 1
			} elseif (is_null($value)) {
253
				$result[] = sprintf('`%s` IS ?', $key);
254
			} elseif (is_array($value) && !empty($value)) {
255 13
				$result[] = sprintf('`%s` IN (%s)', $key, implode(',', array_fill(0, count($value), '?')));
256 13
			} else {
257 12
				throw new ActiveRecordException(sprintf('Search option value of key `%s` is not supported.', $key));
258
			}
259
		}
260
261
		return empty($result) ? '' : 'WHERE ' . implode(' AND ', $result);
262
	}
263
264
	/**
265
	 * Returns the search query order by clause.
266
	 *
267
	 * @param array $orderBy
268
	 * @return string the search query order by clause.
269
	 */
270
	private function getSearchQueryOrderByClause($orderBy)
271
	{
272
		$result = [];
273
274
		foreach ($orderBy as $key => $value) {
275
			$result[] = sprintf('`%s` %s', $key, $value == 'DESC' ? 'DESC' : 'ASC');
276
		}
277
278
		return empty($result) ? '' : 'ORDER BY ' . implode(', ', $result);
279
	}
280
281
	/**
282
	 * Returns the search query limit and clause.
283
	 *
284
	 * @param int $limit = -1
285
	 * @param int $offset = 0
286
	 * @return string the search query limit and clause.
287
	 */
288
	private function getSearchQueryLimitClause($limit, $offset)
289
	{
290
		if ($limit == -1) {
291
			return '';
292
		}
293
294
		return sprintf('LIMIT %d OFFSET %d', $limit, $offset);
295
	}
296
297
	/**
298
	 * Returns the PDO.
299
	 *
300
	 * @return \PDO the PDO.
301
	 */
302
	public function getPdo()
303
	{
304
		return $this->pdo;
305
	}
306
307
	/**
308
	 * Set the PDO.
309
	 *
310
	 * @param \PDO $pdo
311
	 * @return $this
312
	 */
313
	protected function setPdo($pdo)
314
	{
315
		$this->pdo = $pdo;
316
317
		return $this;
318
	}
319
320
	/**
321
	 * Returns the ID.
322
	 *
323
	 * @return null|int The ID.
324
	 */
325
	public function getId()
326
	{
327
		return $this->id;
328
	}
329
330
	/**
331
	 * Set the ID.
332
	 *
333
	 * @param int $id
334
	 * @return $this
335
	 */
336
	protected function setId($id)
337
	{
338
		$this->id = $id;
339
340
		return $this;
341
	}
342
343
	/**
344
	 * Returns the active record name.
345
	 *
346
	 * @return string the active record name.
347
	 */
348
	abstract protected function getActiveRecordName();
349
350
	/**
351
	 * Returns the active record data.
352
	 *
353
	 * @return array the active record data.
354
	 */
355
	abstract protected function getActiveRecordData();
356
357
	/**
358
	 * Set the active record data.
359
	 *
360
	 * @param array $fetch
361
	 * @return null
362
	 */
363
	protected function setActiveRecordData(array $fetch)
364
	{
365
		$data = $this->getActiveRecordData();
366
367
		foreach ($data as $key => &$value) {
368
			if (!array_key_exists($key, $fetch)) {
369
				throw new ActiveRecordException(sprintf('Can not read the expected column `%s`. It\'s not returnd by the `%s` table', $key, $this->getActiveRecordName()));
370
			}
371
372
			$value = $fetch[$key];
373
		}
374
	}
375
}
376