Completed
Push — v2 ( 7e0bb1...9c04df )
by Berend
09:41
created

buildCreateTableColumnEntry()   A

Complexity

Conditions 6
Paths 32

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 26
c 0
b 0
f 0
ccs 14
cts 14
cp 1
rs 9.2222
cc 6
nc 32
nop 5
crap 6
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
 */
9
10
namespace miBadger\ActiveRecord;
11
12
use miBadger\Query\Query;
13
14
/**
15
 * The abstract active record class.
16
 *
17
 * @since 1.0.0
18
 */
19
abstract class AbstractActiveRecord implements ActiveRecordInterface
20
{
21
	const COLUMN_NAME_ID = 'id';
22
	const COLUMN_TYPE_ID = 'INT UNSIGNED';
23
24
	/** @var \PDO The PDO object. */
25
	protected $pdo;
26
27
	/** @var null|int The ID. */
28
	private $id;
29
30
	/** @var array A map of column name to functions that hook the insert function */
31
	protected $registeredCreateHooks;
32
33
	/** @var array A map of column name to functions that hook the read function */
34
	protected $registeredReadHooks;
35
36
	/** @var array A map of column name to functions that hook the update function */
37
	protected $registeredUpdateHooks;
38
39
	/** @var array A map of column name to functions that hook the update function */
40
	protected $registeredDeleteHooks;	
41
42
	/** @var array A map of column name to functions that hook the search function */
43
	protected $registeredSearchHooks;
44
45
	/** @var array A list of table column definitions */
46
	protected $tableDefinition;
47
48
	/**
49
	 * Construct an abstract active record with the given PDO.
50
	 *
51
	 * @param \PDO $pdo
52
	 */
53 72
	public function __construct(\PDO $pdo)
54
	{
55 72
		$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
56 72
		$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
57
58 72
		$this->setPdo($pdo);
59 72
		$this->tableDefinition = $this->getActiveRecordTableDefinition();
60 72
		$this->registeredCreateHooks = [];
61 72
		$this->registeredReadHooks = [];
62 72
		$this->registeredUpdateHooks = [];
63 72
		$this->registeredDeleteHooks = [];
64 72
		$this->registeredSearchHooks = [];
65
66
		// Extend table definition with default ID field, throw exception if field already exists
67 72
		if (array_key_exists('id', $this->tableDefinition)) {
68
			$message = "Table definition in record contains a field with name \"id\"";
69
			$message .= ", which is a reserved name by ActiveRecord";
70
			throw new ActiveRecordException($message, 0);
71
		}
72
73 72
		$this->tableDefinition[self::COLUMN_NAME_ID] =
74
		[
75 72
			'value' => &$this->id,
76
			'validate' => null,
77 72
			'type' => self::COLUMN_TYPE_ID,
78 72
			'properties' => ColumnProperty::NOT_NULL | ColumnProperty::IMMUTABLE | ColumnProperty::AUTO_INCREMENT | ColumnProperty::PRIMARY_KEY
79
		];
80 72
	}
81
82 28
	private function checkHookConstraints($columnName, $hookMap)
83
	{
84
		// Check whether column exists
85 28
		if (!array_key_exists($columnName, $this->tableDefinition)) 
86
		{
87 5
			throw new ActiveRecordException("Hook is trying to register on non-existing column \"$columnName\"", 0);
88
		}
89
90
		// Enforcing 1 hook per table column
91 23
		if (array_key_exists($columnName, $hookMap)) {
92 5
			$message = "Hook is trying to register on an already registered column \"$columnName\", ";
93 5
			$message .= "do you have conflicting traits?";
94 5
			throw new ActiveRecordException($message, 0);
95
		}
96 23
	}
97
98
	/**
99
	 * Register a new hook for a specific column that gets called before execution of the create() method
100
	 * Only one hook per column can be registered at a time
101
	 * @param string $columnName The name of the column that is registered.
102
	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
103
	 */
104 6
	public function registerCreateHook($columnName, $fn)
105
	{
106 6
		$this->checkHookConstraints($columnName, $this->registeredCreateHooks);
107
108 5
		if (is_string($fn) && is_callable([$this, $fn])) {
109 3
			$this->registeredCreateHooks[$columnName] = [$this, $fn];
110 2
		} else if (is_callable($fn)) {
111 2
			$this->registeredCreateHooks[$columnName] = $fn;
112
		} else {
113
			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
114
		}
115 5
	}
116
117
	/**
118
	 * Register a new hook for a specific column that gets called before execution of the read() method
119
	 * Only one hook per column can be registered at a time
120
	 * @param string $columnName The name of the column that is registered.
121
	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
122
	 */
123 3
	public function registerReadHook($columnName, $fn)
124
	{
125 3
		$this->checkHookConstraints($columnName, $this->registeredReadHooks);
126
127 2
		if (is_string($fn) && is_callable([$this, $fn])) {
128
			$this->registeredReadHooks[$columnName] = [$this, $fn];
129 2
		} else if (is_callable($fn)) {
130 2
			$this->registeredReadHooks[$columnName] = $fn;
131
		} else {
132
			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
133
		}
134 2
	}
135
136
	/**
137
	 * Register a new hook for a specific column that gets called before execution of the update() method
138
	 * Only one hook per column can be registered at a time
139
	 * @param string $columnName The name of the column that is registered.
140
	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
141
	 */
142 6
	public function registerUpdateHook($columnName, $fn)
143
	{
144 6
		$this->checkHookConstraints($columnName, $this->registeredUpdateHooks);
145
146 5
		if (is_string($fn) && is_callable([$this, $fn])) {
147 3
			$this->registeredUpdateHooks[$columnName] = [$this, $fn];
148 2
		} else if (is_callable($fn)) {
149 2
			$this->registeredUpdateHooks[$columnName] = $fn;
150
		} else {
151
			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
152
		}
153 5
	}
154
155
	/**
156
	 * Register a new hook for a specific column that gets called before execution of the delete() method
157
	 * Only one hook per column can be registered at a time
158
	 * @param string $columnName The name of the column that is registered.
159
	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object.
160
	 */
161 3
	public function registerDeleteHook($columnName, $fn)
162
	{
163 3
		$this->checkHookConstraints($columnName, $this->registeredDeleteHooks);
164
165 2
		if (is_string($fn) && is_callable([$this, $fn])) {
166
			$this->registeredDeleteHooks[$columnName] = [$this, $fn];
167 2
		} else if (is_callable($fn)) {
168 2
			$this->registeredDeleteHooks[$columnName] = $fn;
169
		} else {
170
			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
171
		}
172 2
	}
173
174
	/**
175
	 * Register a new hook for a specific column that gets called before execution of the search() method
176
	 * Only one hook per column can be registered at a time
177
	 * @param string $columnName The name of the column that is registered.
178
	 * @param string|callable $fn Either a callable, or the name of a method on the inheriting object. The callable is required to take one argument: an instance of miBadger\Query\Query; 
179
	 */
180 13
	public function registerSearchHook($columnName, $fn)
181
	{
182 13
		$this->checkHookConstraints($columnName, $this->registeredSearchHooks);
183
184 12
		if (is_string($fn) && is_callable([$this, $fn])) {
185 10
			$this->registeredSearchHooks[$columnName] = [$this, $fn];
186 2
		} else if (is_callable($fn)) {
187 2
			$this->registeredSearchHooks[$columnName] = $fn;
188
		} else {
189
			throw new ActiveRecordException("Provided hook on column \"$columnName\" is not callable", 0);
190
		}
191 12
	}
192
193
	/**
194
	 * Adds a new column definition to the table.
195
	 * @param string $columnName The name of the column that is registered.
196
	 * @param Array $definition The definition of that column.
197
	 */
198 38
	public function extendTableDefinition($columnName, $definition)
199
	{
200 38
		if ($this->tableDefinition === null) {
201
			throw new ActiveRecordException("tableDefinition is null, most likely due to parent class not having been initialized in constructor");
202
		}
203
204
		// Enforcing table can only be extended with new columns
205 38
		if (array_key_exists($columnName, $this->tableDefinition)) {
206 1
			$message = "Table is being extended with a column that already exists, ";
207 1
			$message .= "\"$columnName\" conflicts with your table definition";
208 1
			throw new ActiveRecordException($message, 0);
209
		}
210
211 38
		$this->tableDefinition[$columnName] = $definition;
212 38
	}
213
214
	/**
215
	 * Returns the type string as it should appear in the mysql create table statement for the given column
216
	 * @return string The type string
217
	 */
218 23
	private function getDatabaseTypeString($colName, $type, $length)
219
	{
220 23
		switch (strtoupper($type)) {
221 23
			case '':
222 1
				throw new ActiveRecordException(sprintf("Column %s has invalid type \"NULL\"", $colName));
223
				
224 22
			case 'DATETIME':
225 22
			case 'DATE':
226 22
			case 'TIME':
227 22
			case 'TEXT':
228 22
			case 'INT UNSIGNED':
229 22
				return $type;
230
231 21
			case 'VARCHAR':
232 21
				if ($length === null) {
233
					throw new ActiveRecordException(sprintf("field type %s requires specified column field \"LENGTH\"", $colName));
234
				} else {
235 21
					return sprintf('%s(%d)', $type, $length);	
236
				}
237
238 9
			case 'INT':
239
			case 'TINYINT':
240
			case 'BIGINT':
241
			default: 	
242
				// @TODO(Default): throw exception, or implicitly assume that type is correct? (For when using SQL databases with different types)
243 9
				if ($length === null) {
244
					return $type;
245
				} else {
246 9
					return sprintf('%s(%d)', $type, $length);	
247
				}
248
		}
249
	}
250
251
	/**
252
	 * Builds the part of a MySQL create table statement that corresponds to the supplied column
253
	 * @param string $colName 	Name of the database column
254
	 * @param string $type 		The type of the string
255
	 * @param int $properties 	The set of Column properties that apply to this column (See ColumnProperty for options)
256
	 * @return string
257
	 */
258 23
	private function buildCreateTableColumnEntry($colName, $type, $length, $properties, $default)
259
	{
260 23
		$stmnt = sprintf('`%s` %s ', $colName, $this->getDatabaseTypeString($colName, $type, $length));
261 22
		if ($properties & ColumnProperty::NOT_NULL) {
262 22
			$stmnt .= 'NOT NULL ';
263
		} else {
264 22
			$stmnt .= 'NULL ';
265
		}
266
267 22
		if ($default !== NULL) {
268 15
			$stmnt .= ' DEFAULT ' . $default . ' ';
269
		}
270
271 22
		if ($properties & ColumnProperty::AUTO_INCREMENT) {
272 22
			$stmnt .= 'AUTO_INCREMENT ';
273
		}
274
275 22
		if ($properties & ColumnProperty::UNIQUE) {
276 8
			$stmnt .= 'UNIQUE ';
277
		}
278
279 22
		if ($properties & ColumnProperty::PRIMARY_KEY) {
280 22
			$stmnt .= 'PRIMARY KEY ';
281
		}
282
283 22
		return $stmnt;
284
	}
285
286
	/**
287
	 * Sorts the column statement components in the order such that the id appears first, 
288
	 * 		followed by all other columns in alphabetical ascending order
289
	 * @param   Array $colStatements Array of column statements
290
	 * @return  Array
291
	 */
292 22
	private function sortColumnStatements($colStatements)
293
	{
294
		// Find ID statement and put it first
295 22
		$sortedStatements = [];
296
297 22
		$sortedStatements[] = $colStatements[self::COLUMN_NAME_ID];
298 22
		unset($colStatements[self::COLUMN_NAME_ID]);
299
300
		// Sort remaining columns in alphabetical order
301 22
		$columns = array_keys($colStatements);
302 22
		sort($columns);
303 22
		foreach ($columns as $colName) {
304 22
			$sortedStatements[] = $colStatements[$colName];
305
		}
306
307 22
		return $sortedStatements;
308
	}
309
310
	/**
311
	 * Builds the MySQL Create Table statement for the internal table definition
312
	 * @return string
313
	 */
314 23
	public function buildCreateTableSQL()
315
	{
316 23
		$columnStatements = [];
317 23
		foreach ($this->tableDefinition as $colName => $definition) {
318
			// Destructure column definition
319 23
			$type    = $definition['type'] ?? null;
320 23
			$default = $definition['default'] ?? null;
321 23
			$length  = $definition['length'] ?? null;
322 23
			$properties = $definition['properties'] ?? null;
323
324 23
			if (isset($definition['relation']) && $type !== null) {
325
				$msg = "Column \"$colName\": ";
326
				$msg .= "Relationship columns have an automatically inferred type, so type should be omitted";
327
				throw new ActiveRecordException($msg);
328 23
			} else if (isset($definition['relation'])) {
329 2
				$type = self::COLUMN_TYPE_ID;
330
			}
331
332 23
			$columnStatements[$colName] = $this->buildCreateTableColumnEntry($colName, $type, $length, $properties, $default);
333
		}
334
335
		// Sort table (first column is id, the remaining are alphabetically sorted)
336 22
		$columnStatements = $this->sortColumnStatements($columnStatements);
337
338 22
		$sql = 'CREATE TABLE ' . $this->getActiveRecordTable() . ' ';
339 22
		$sql .= "(\n";
340 22
		$sql .= join($columnStatements, ",\n");
0 ignored issues
show
Bug introduced by
$columnStatements of type array is incompatible with the type string expected by parameter $glue of join(). ( Ignorable by Annotation )

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

340
		$sql .= join(/** @scrutinizer ignore-type */ $columnStatements, ",\n");
Loading history...
Bug introduced by
', ' of type string is incompatible with the type array expected by parameter $pieces of join(). ( Ignorable by Annotation )

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

340
		$sql .= join($columnStatements, /** @scrutinizer ignore-type */ ",\n");
Loading history...
341 22
		$sql .= "\n);";
342
343 22
		return $sql;
344
	}
345
346
	/**
347
	 * Creates the entity as a table in the database
348
	 */
349 23
	public function createTable()
350
	{
351 23
		$this->pdo->query($this->buildCreateTableSQL());
352 22
	}
353
354
	/**
355
	 * builds a MySQL constraint statement for the given parameters
356
	 * @param string $parentTable
357
	 * @param string $parentColumn
358
	 * @param string $childTable
359
	 * @param string $childColumn
360
	 * @return string The MySQL table constraint string
361
	 */
362 4
	protected function buildConstraint($parentTable, $parentColumn, $childTable, $childColumn)
363
	{
364
		$template = <<<SQL
365 4
ALTER TABLE `%s`
366
ADD CONSTRAINT
367
FOREIGN KEY (`%s`)
368
REFERENCES `%s`(`%s`)
369
ON DELETE CASCADE;
370
SQL;
371 4
		return sprintf($template, $childTable, $childColumn, $parentTable, $parentColumn);
372
	}
373
374
	/**
375
	 * Iterates over the specified constraints in the table definition, 
376
	 * 		and applies these to the database.
377
	 */
378 1
	public function createTableConstraints()
379
	{
380
		// Iterate over columns, check whether "relation" field exists, if so create constraint
381 1
		foreach ($this->tableDefinition as $colName => $definition) {
382 1
			if (isset($definition['relation']) && $definition['relation'] instanceof AbstractActiveRecord) {
383
				// Forge new relation
384 1
				$target = $definition['relation'];
385 1
				$constraintSql = $this->buildConstraint($target->getActiveRecordTable(), 'id', $this->getActiveRecordTable(), $colName);
386
387 1
				$this->pdo->query($constraintSql);
388
			}
389
		}
390 1
	}
391
392
	/**
393
	 * Returns the name -> variable mapping for the table definition.
394
	 * @return Array The mapping
395
	 */
396 44
	protected function getActiveRecordColumns()
397
	{
398 44
		$bindings = [];
399 44
		foreach ($this->tableDefinition as $colName => $definition) {
400
401
			// Ignore the id column (key) when inserting or updating
402 44
			if ($colName == self::COLUMN_NAME_ID) {
403 44
				continue;
404
			}
405
406 44
			$bindings[$colName] = &$definition['value'];
407
		}
408 44
		return $bindings;
409
	}
410
411
	/**
412
	 * {@inheritdoc}
413
	 */
414 19
	public function create()
415
	{
416 19
		foreach ($this->registeredCreateHooks as $colName => $fn) {
417
			// @TODO: Would it be better to pass the Query to the function?
418 3
			$fn();
419
		}
420
421
		try {
422 19
			(new Query($this->getPdo(), $this->getActiveRecordTable()))
423 19
				->insert($this->getActiveRecordColumns())
424 19
				->execute();
425
426 17
			$this->setId(intval($this->getPdo()->lastInsertId()));
427 2
		} catch (\PDOException $e) {
428 2
			throw new ActiveRecordException($e->getMessage(), 0, $e);
429
		}
430
431 17
		return $this;
432
	}
433
434
	/**
435
	 * {@inheritdoc}
436
	 */
437 17
	public function read($id)
438
	{
439 17
		foreach ($this->registeredReadHooks as $colName => $fn) {
440
			// @TODO: Would it be better to pass the Query to the function?
441 1
			$fn();
442
		}
443
444
		try {
445 17
			$row = (new Query($this->getPdo(), $this->getActiveRecordTable()))
446 17
				->select()
447 17
				->where('id', '=', $id)
448 17
				->execute()
449 16
				->fetch();
450
451 16
			if ($row === false) {
452 3
				throw new ActiveRecordException(sprintf('Can not read the non-existent active record entry %d from the `%s` table.', $id, $this->getActiveRecordTable()));
453
			}
454
455 13
			$this->fill($row)->setId($id);
456 4
		} catch (\PDOException $e) {
457 1
			throw new ActiveRecordException($e->getMessage(), 0, $e);
458
		}
459
460 13
		return $this;
461
	}
462
463
	/**
464
	 * {@inheritdoc}
465
	 */
466 8
	public function update()
467
	{
468 8
		foreach ($this->registeredUpdateHooks as $colName => $fn) {
469
			// @TODO: Would it be better to pass the Query to the function?
470 2
			$fn();
471
		}
472
473
		try {
474 8
			(new Query($this->getPdo(), $this->getActiveRecordTable()))
475 8
				->update($this->getActiveRecordColumns())
476 8
				->where('id', '=', $this->getId())
477 8
				->execute();
478 2
		} catch (\PDOException $e) {
479 2
			throw new ActiveRecordException($e->getMessage(), 0, $e);
480
		}
481
482 6
		return $this;
483
	}
484
485
	/**
486
	 * {@inheritdoc}
487
	 */
488 6
	public function delete()
489
	{
490 6
		foreach ($this->registeredDeleteHooks as $colName => $fn) {
491
			// @TODO: Would it be better to pass the Query to the function?
492 1
			$fn();
493
		}
494
495
		try {
496 6
			(new Query($this->getPdo(), $this->getActiveRecordTable()))
497 6
				->delete()
498 6
				->where('id', '=', $this->getId())
499 6
				->execute();
500
501 5
			$this->setId(null);
502 1
		} catch (\PDOException $e) {
503 1
			throw new ActiveRecordException($e->getMessage(), 0, $e);
504
		}
505
506 5
		return $this;
507
	}
508
509
	/**
510
	 * {@inheritdoc}
511
	 */
512 2
	public function sync()
513
	{
514 2
		if (!$this->exists()) {
515 1
			return $this->create();
516
		}
517
518 1
		return $this->update();
519
	}
520
521
	/**
522
	 * {@inheritdoc}
523
	 */
524 3
	public function exists()
525
	{
526 3
		return $this->getId() !== null;
527
	}
528
529
	/**
530
	 * {@inheritdoc}
531
	 */
532 26
	public function fill(array $attributes)
533
	{
534 26
		$columns = $this->getActiveRecordColumns();
535 26
		$columns['id'] = &$this->id;
536
537 26
		foreach ($attributes as $key => $value) {
538 26
			if (array_key_exists($key, $columns)) {
539 26
				$columns[$key] = $value;
540
			}
541
		}
542
543 26
		return $this;
544
	}
545
546
	/**
547
	 * {@inheritdoc}
548
	 */
549 3
	public function searchOne(array $where = [], array $orderBy = [])
550
	{
551
		try {
552 3
			$row = $this->getSearchQueryResult($where, $orderBy, 1)->fetch();
553
554 2
			if ($row === false) {
555 1
				throw new ActiveRecordException(sprintf('Can not search one non-existent entry from the `%s` table.', $this->getActiveRecordTable()));
556
			}
557
558 1
			return $this->fill($row)->setId($row['id']);
559 2
		} catch (\PDOException $e) {
560 1
			throw new ActiveRecordException($e->getMessage(), 0, $e);
561
		}
562
	}
563
564
	/**
565
	 * {@inheritdoc}
566
	 */
567 13
	public function search(array $where = [], array $orderBy = [], $limit = -1, $offset = 0)
568
	{
569
		try {
570 13
			$queryResult = $this->getSearchQueryResult($where, $orderBy, $limit, $offset);
571 11
			$result = [];
572
573 11
			foreach ($queryResult as $row) {
574 11
				$new = clone $this;
575
576 11
				$result[] = $new->fill($row)->setId($row['id']);
577
			}
578
579 11
			return $result;
580 2
		} catch (\PDOException $e) {
581 2
			throw new ActiveRecordException($e->getMessage(), 0, $e);
582
		}
583
	}
584
585
	/**
586
	 * Returns the search query result with the given where, order by, limit and offset clauses.
587
	 *
588
	 * @param array $where = []
589
	 * @param array $orderBy = []
590
	 * @param int $limit = -1
591
	 * @param int $offset = 0
592
	 * @return \miBadger\Query\QueryResult the search query result with the given where, order by, limit and offset clauses.
593
	 */
594 16
	private function getSearchQueryResult(array $where = [], array $orderBy = [], $limit = -1, $offset = 0)
595
	{
596 16
		$query = (new Query($this->getPdo(), $this->getActiveRecordTable()))
597 16
			->select();
598
599 16
		$this->getSearchQueryWhere($query, $where);
600 16
		$this->getSearchQueryOrderBy($query, $orderBy);
601 16
		$this->getSearchQueryLimit($query, $limit, $offset);
602
603
		// Ignore all trait modifiers for which a where clause was specified
604 16
		$registeredSearchHooks = $this->registeredSearchHooks;
605 16
		foreach ($where as $index => $clause) {
606 9
			$colName = $clause[0];
607 9
			unset($registeredSearchHooks[$colName]);
608
		}
609
610
		// Allow traits to modify the query
611 16
		foreach ($registeredSearchHooks as $column => $searchFunction) {
612 1
			$searchFunction($query);
613
		}
614
615 16
		return $query->execute();
616
	}
617
618
	/**
619
	 * Returns the given query after adding the given where conditions.
620
	 *
621
	 * @param \miBadger\Query\Query $query
622
	 * @param array $where
623
	 * @return \miBadger\Query\Query the given query after adding the given where conditions.
624
	 */
625 16
	private function getSearchQueryWhere($query, $where)
626
	{
627 16
		foreach ($where as $key => $value) {
628 9
			$query->where($value[0], $value[1], $value[2]);
629
		}
630
631 16
		return $query;
632
	}
633
634
	/**
635
	 * Returns the given query after adding the given order by conditions.
636
	 *
637
	 * @param \miBadger\Query\Query $query
638
	 * @param array $orderBy
639
	 * @return \miBadger\Query\Query the given query after adding the given order by conditions.
640
	 */
641 16
	private function getSearchQueryOrderBy($query, $orderBy)
642
	{
643 16
		foreach ($orderBy as $key => $value) {
644 1
			$query->orderBy($key, $value);
645
		}
646
647 16
		return $query;
648
	}
649
650
	/**
651
	 * Returns the given query after adding the given limit and offset conditions.
652
	 *
653
	 * @param \miBadger\Query\Query $query
654
	 * @param int $limit
655
	 * @param int $offset
656
	 * @return \miBadger\Query\Query the given query after adding the given limit and offset conditions.
657
	 */
658 16
	private function getSearchQueryLimit($query, $limit, $offset)
659
	{
660 16
		if ($limit > -1) {
661 5
			$query->limit($limit);
662 5
			$query->offset($offset);
663
		}
664
665 16
		return $query;
666
	}
667
668
	/**
669
	 * Returns the PDO.
670
	 *
671
	 * @return \PDO the PDO.
672
	 */
673 50
	public function getPdo()
674
	{
675 50
		return $this->pdo;
676
	}
677
678
	/**
679
	 * Set the PDO.
680
	 *
681
	 * @param \PDO $pdo
682
	 * @return $this
683
	 */
684 72
	protected function setPdo($pdo)
685
	{
686 72
		$this->pdo = $pdo;
687
688 72
		return $this;
689
	}
690
691
	/**
692
	 * Returns the ID.
693
	 *
694
	 * @return null|int The ID.
695
	 */
696 21
	public function getId()
697
	{
698 21
		return $this->id;
699
	}
700
701
	/**
702
	 * Set the ID.
703
	 *
704
	 * @param int $id
705
	 * @return $this
706
	 */
707 39
	protected function setId($id)
708
	{
709 39
		$this->id = $id;
710
711 39
		return $this;
712
	}
713
714
	/**
715
	 * Returns the active record table.
716
	 *
717
	 * @return string the active record table name.
718
	 */
719
	abstract protected function getActiveRecordTable();
720
721
	/**
722
	 * Returns the active record columns.
723
	 *
724
	 * @return array the active record columns.
725
	 */
726
	abstract protected function getActiveRecordTableDefinition();
727
}
728