1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Starlit Db. |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright (c) 2016 Starweb AB |
6
|
|
|
* @license BSD 3-Clause |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Starlit\Db; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* A database entity fetcher is intended to fetch new entities from the database. |
13
|
|
|
* |
14
|
|
|
* @author Andreas Nilsson <http://github.com/jandreasn> |
15
|
|
|
*/ |
16
|
|
|
abstract class AbstractDbEntityFetcher |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* The database adapter/connection/handler. |
20
|
|
|
* |
21
|
|
|
* @var Db |
22
|
|
|
*/ |
23
|
|
|
protected $db; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Database entity's class name (optional, meant to be overridden). |
27
|
|
|
* |
28
|
|
|
* @var string |
29
|
|
|
*/ |
30
|
|
|
protected $dbEntityClass; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Constructor. |
34
|
|
|
* |
35
|
|
|
* @param Db $db |
36
|
|
|
*/ |
37
|
13 |
|
public function __construct(Db $db) |
38
|
|
|
{ |
39
|
13 |
|
$this->db = $db; |
40
|
13 |
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Helper method to get LIMIT SQL and paging flag from limit parameter. |
44
|
|
|
* |
45
|
|
|
* @param int $limit A limit number (like 5) |
46
|
|
|
* @param array $pageItem A pagination array like [1, 10], where 1 is the page number and 10 the number of |
47
|
|
|
* rows per page (first page is 1). |
48
|
|
|
* @return string |
49
|
|
|
*/ |
50
|
3 |
|
protected function getLimitSql($limit, array $pageItem = []) |
51
|
|
|
{ |
52
|
3 |
|
$limitSql = ''; |
53
|
3 |
|
if (!empty($limit) || !empty($pageItem)) { |
54
|
3 |
|
$limitSql = 'LIMIT '; |
55
|
3 |
|
if (!empty($pageItem)) { |
56
|
2 |
|
list($pageNo, $rowsPerPage) = $pageItem; |
57
|
|
|
|
58
|
2 |
|
$pageNo = (int) $pageNo; |
59
|
2 |
|
if ($pageNo < 1) { |
60
|
1 |
|
throw new \InvalidArgumentException("Invalid pagination page number \"{$pageNo}\""); |
61
|
|
|
} |
62
|
1 |
|
$rowsPerPage = (int) $rowsPerPage; |
63
|
|
|
|
64
|
|
|
// Offset example: "10" skips the first 10 rows, start showing the 11th |
65
|
1 |
|
$offset = ($pageNo - 1) * $rowsPerPage; |
66
|
1 |
|
$limitSql .= sprintf('%d, %d', $offset, $rowsPerPage); |
67
|
|
|
} else { |
68
|
1 |
|
$limitSql .= (int) $limit; |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
|
72
|
2 |
|
return $limitSql; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Helper method to get pagination result array if pagination is requested. |
77
|
|
|
* |
78
|
|
|
* @param array $objects |
79
|
|
|
* @param bool $pagination |
80
|
|
|
* @return array |
81
|
|
|
*/ |
82
|
2 |
View Code Duplication |
protected function getFetchPaginationResult(array $objects, $pagination) |
|
|
|
|
83
|
|
|
{ |
84
|
2 |
|
if ($pagination) { |
85
|
1 |
|
$totalRowCount = $this->db->fetchValue('SELECT FOUND_ROWS()'); |
86
|
|
|
|
87
|
1 |
|
return [$objects, $totalRowCount]; |
88
|
|
|
} else { |
89
|
1 |
|
return $objects; |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Helper method to get pagination result as objects if pagination is requested. |
95
|
|
|
* |
96
|
|
|
* @param array $rows |
97
|
|
|
* @param bool $pagination |
98
|
|
|
* @return array |
99
|
|
|
*/ |
100
|
2 |
View Code Duplication |
protected function getDbEntitiesFromRowsPaginated(array $rows, $pagination) |
|
|
|
|
101
|
|
|
{ |
102
|
2 |
|
if ($pagination) { |
103
|
1 |
|
$totalRowCount = $this->db->fetchValue('SELECT FOUND_ROWS()'); |
104
|
1 |
|
return [$this->getDbEntitiesFromRows($rows), $totalRowCount]; |
105
|
|
|
} |
106
|
|
|
|
107
|
1 |
|
return $this->getDbEntitiesFromRows($rows); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @param array $rows |
112
|
|
|
* @param string $keyPropertyName Optional, will default to primary if not provided |
113
|
|
|
* @return array |
114
|
|
|
*/ |
115
|
6 |
|
protected function getDbEntitiesFromRows(array &$rows, $keyPropertyName = null) |
116
|
|
|
{ |
117
|
6 |
|
if (!$this->dbEntityClass) { |
118
|
1 |
|
throw new \LogicException('No db entity class set'); |
119
|
|
|
} |
120
|
|
|
|
121
|
5 |
|
$entityClass = $this->dbEntityClass; |
122
|
5 |
|
if (!empty($keyPropertyName)) { |
123
|
1 |
|
$keyDbFieldName = AbstractDbEntity::getDbFieldName($keyPropertyName); |
124
|
|
|
} else { |
125
|
4 |
|
$keyDbFieldName = $entityClass::getPrimaryDbFieldKey(); |
126
|
|
|
} |
127
|
|
|
|
128
|
5 |
|
$keyIsArray = is_array($keyDbFieldName); |
129
|
5 |
|
$dbObjects = []; |
130
|
5 |
|
foreach ($rows as $row) { |
131
|
5 |
|
if ($keyIsArray) { |
132
|
1 |
|
$key = implode('-', array_intersect_key($row, array_flip($keyDbFieldName))); |
133
|
|
|
} else { |
134
|
4 |
|
$key = $row[$keyDbFieldName]; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
// If multiple rows with same key, we assume the secondary rows are secondary |
138
|
|
|
// information (like language rows), which we can add to the same object by |
139
|
|
|
// calling setRawDbData again manually. |
140
|
5 |
|
if (isset($dbObjects[$key])) { |
141
|
1 |
|
$this->setDbEntityDataFromRow($dbObjects[$key], $row); |
142
|
|
|
} else { |
143
|
5 |
|
$dbObjects[$key] = $this->createNewDbEntity($row); |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
5 |
|
return $dbObjects; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* @param array $rowData |
152
|
|
|
* @return AbstractDbEntity |
153
|
|
|
*/ |
154
|
6 |
|
protected function createNewDbEntity(array $rowData) |
155
|
|
|
{ |
156
|
6 |
|
$entityClass = $this->dbEntityClass; |
157
|
6 |
|
$dbEntity = new $entityClass(); |
158
|
6 |
|
$this->setDbEntityDataFromRow($dbEntity, $rowData); |
159
|
|
|
|
160
|
6 |
|
return $dbEntity; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @param AbstractDbEntity $dbEntity |
165
|
|
|
* @param array $rowData |
166
|
|
|
*/ |
167
|
6 |
|
protected function setDbEntityDataFromRow(AbstractDbEntity $dbEntity, array $rowData) |
168
|
|
|
{ |
169
|
6 |
|
$dbEntity->setDbDataFromRow($rowData); |
170
|
6 |
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @param array|false $row |
174
|
|
|
* @return AbstractDbEntity|null |
175
|
|
|
*/ |
176
|
2 |
|
protected function getDbEntityFromRow($row) |
177
|
|
|
{ |
178
|
2 |
|
if (!$this->dbEntityClass) { |
179
|
1 |
|
throw new \LogicException('No db entity class set'); |
180
|
|
|
} |
181
|
|
|
|
182
|
1 |
|
return $row ? $this->createNewDbEntity($row) : null; |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
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.