1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace WebComplete\core\entity; |
4
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Connection; |
6
|
|
|
use Doctrine\DBAL\Query\QueryBuilder; |
7
|
|
|
use WebComplete\core\condition\ConditionDbParser; |
8
|
|
|
use WebComplete\core\factory\ObjectFactory; |
9
|
|
|
use WebComplete\core\utils\hydrator\HydratorInterface; |
10
|
|
|
use WebComplete\core\condition\Condition; |
11
|
|
|
|
12
|
|
|
abstract class AbstractEntityRepositoryDb extends AbstractEntityRepository |
13
|
|
|
{ |
14
|
|
|
const SERIALIZE_STRATEGY_JSON = 1; |
15
|
|
|
const SERIALIZE_STRATEGY_PHP = 2; |
16
|
|
|
|
17
|
|
|
protected $table; |
18
|
|
|
/** |
19
|
|
|
* map: fieldName: propertyName |
20
|
|
|
* @var array|null |
21
|
|
|
*/ |
22
|
|
|
protected $map; |
23
|
|
|
protected $serializeFields = []; |
24
|
|
|
protected $serializeStrategy = self::SERIALIZE_STRATEGY_JSON; |
25
|
|
|
|
26
|
|
|
/** @var Connection */ |
27
|
|
|
protected $db; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var ConditionDbParser |
31
|
|
|
*/ |
32
|
|
|
protected $conditionParser; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @param ObjectFactory $factory |
36
|
|
|
* @param HydratorInterface $hydrator |
37
|
|
|
* @param ConditionDbParser $conditionParser |
38
|
|
|
* @param Connection $db |
39
|
|
|
*/ |
40
|
|
|
public function __construct( |
41
|
|
|
ObjectFactory $factory, |
42
|
|
|
HydratorInterface $hydrator, |
43
|
|
|
ConditionDbParser $conditionParser, |
44
|
|
|
Connection $db |
45
|
|
|
) { |
46
|
|
|
parent::__construct($factory, $hydrator); |
47
|
|
|
$this->db = $db; |
48
|
|
|
$this->conditionParser = $conditionParser; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param \Closure $closure |
53
|
|
|
* @throws \Exception |
54
|
|
|
*/ |
55
|
|
|
public function transaction(\Closure $closure) |
56
|
|
|
{ |
57
|
|
|
$this->db->transactional($closure); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param $id |
62
|
|
|
* @return AbstractEntity|null |
63
|
|
|
*/ |
64
|
|
|
public function findById($id) |
65
|
|
|
{ |
66
|
|
|
return $this->findOne(new Condition(['t1.id' => $id])); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @param Condition $condition |
71
|
|
|
* @return AbstractEntity|null |
72
|
|
|
*/ |
73
|
|
|
public function findOne(Condition $condition) |
74
|
|
|
{ |
75
|
|
|
$result = null; |
76
|
|
|
$select = $this->selectQuery($condition); |
77
|
|
|
if ($row = $select->execute()->fetch()) { |
78
|
|
|
$result = $this->rowToEntity($row); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
return $result; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @param Condition $condition |
86
|
|
|
* @return AbstractEntity[] |
87
|
|
|
*/ |
88
|
|
|
public function findAll(Condition $condition = null): array |
89
|
|
|
{ |
90
|
|
|
$result = []; |
91
|
|
|
$select = $this->selectQuery($condition); |
92
|
|
View Code Duplication |
if ($rows = $select->execute()->fetchAll(\PDO::FETCH_ASSOC)) { |
|
|
|
|
93
|
|
|
foreach ($rows as $row) { |
94
|
|
|
$entity = $this->rowToEntity($row); |
95
|
|
|
$result[$entity->getId()] = $entity; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return $result; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @param Condition|null $condition |
104
|
|
|
* @return int |
105
|
|
|
*/ |
106
|
|
|
public function count(Condition $condition = null): int |
107
|
|
|
{ |
108
|
|
|
$select = $this->selectQuery($condition); |
109
|
|
|
return $select->select(['t1.id'])->execute()->rowCount(); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @param AbstractEntity $item |
114
|
|
|
*/ |
115
|
|
|
public function save(AbstractEntity $item) |
116
|
|
|
{ |
117
|
|
|
$data = $this->hydrator->extract($item, $this->map); |
118
|
|
|
$this->beforeDataSave($data); |
119
|
|
|
$this->serializeFields($data); |
120
|
|
|
if ($id = $item->getId()) { |
121
|
|
|
$this->db->update($this->table, $data, ['t1.id' => $id]); |
122
|
|
|
} else { |
123
|
|
|
unset($data['id']); |
124
|
|
|
$this->db->insert($this->table, $data); |
125
|
|
|
$item->setId((int)$this->db->lastInsertId()); |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* @param $id |
131
|
|
|
* |
132
|
|
|
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException |
133
|
|
|
*/ |
134
|
|
|
public function delete($id) |
135
|
|
|
{ |
136
|
|
|
$this->db->delete($this->table, ['t1.id' => $id]); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* @param Condition|null $condition |
141
|
|
|
*/ |
142
|
|
|
public function deleteAll(Condition $condition = null) |
143
|
|
|
{ |
144
|
|
|
$query = $this->selectQuery($condition); |
145
|
|
|
$query->delete($this->table, 't1'); |
146
|
|
|
$query->execute(); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param string $field |
151
|
|
|
* @param string $key |
152
|
|
|
* @param Condition|null $condition |
153
|
|
|
* |
154
|
|
|
* @return array |
155
|
|
|
* @throws \Exception |
156
|
|
|
*/ |
157
|
|
|
public function getMap(string $field, string $key = 'id', Condition $condition = null): array |
158
|
|
|
{ |
159
|
|
|
$result = []; |
160
|
|
|
$select = $this->selectQuery($condition); |
161
|
|
|
$select->select(['t1.' . $field, 't1.' . $key]); |
162
|
|
View Code Duplication |
if ($rows = $select->execute()->fetchAll(\PDO::FETCH_ASSOC)) { |
|
|
|
|
163
|
|
|
foreach ($rows as $row) { |
164
|
|
|
$this->unserializeFields($row); |
165
|
|
|
$result[$row[$key]] = $row[$field]; |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return $result; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* @param Condition|null $condition |
174
|
|
|
* @return QueryBuilder |
175
|
|
|
*/ |
176
|
|
|
protected function selectQuery(Condition $condition = null): QueryBuilder |
177
|
|
|
{ |
178
|
|
|
$queryBuilder = $this->db->createQueryBuilder(); |
179
|
|
|
$queryBuilder->select(['t1.*'])->from($this->table, 't1'); |
180
|
|
|
$this->conditionParser->parse($queryBuilder, $condition, $this->map); |
181
|
|
|
return $queryBuilder; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Adjust data before save |
186
|
|
|
* @param $data |
187
|
|
|
*/ |
188
|
|
|
protected function beforeDataSave(&$data) |
189
|
|
|
{ |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @param $data |
194
|
|
|
* |
195
|
|
|
* @return AbstractEntity |
196
|
|
|
*/ |
197
|
|
|
private function rowToEntity($data) |
198
|
|
|
{ |
199
|
|
|
$this->unserializeFields($data); |
200
|
|
|
$entity = $this->factory->createFromData($data, $this->map); |
201
|
|
|
/** @var AbstractEntity $entity */ |
202
|
|
|
return $entity; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* @param $data |
207
|
|
|
*/ |
208
|
|
|
private function serializeFields(&$data) |
209
|
|
|
{ |
210
|
|
|
foreach ($this->serializeFields as $field) { |
211
|
|
|
if (isset($data[$field])) { |
212
|
|
|
$data[$field] = $this->serializeStrategy === self::SERIALIZE_STRATEGY_JSON |
213
|
|
|
? \json_encode($data[$field]) |
214
|
|
|
: \serialize($data[$field]); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @param $row |
221
|
|
|
*/ |
222
|
|
|
private function unserializeFields(&$row) |
223
|
|
|
{ |
224
|
|
|
foreach ($this->serializeFields as $field) { |
225
|
|
|
if (isset($row[$field])) { |
226
|
|
|
$row[$field] = $this->serializeStrategy === self::SERIALIZE_STRATEGY_JSON |
227
|
|
|
? \json_decode($row[$field], true) |
228
|
|
|
: \unserialize($row[$field], ['allowed_classes' => true]); |
|
|
|
|
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
} |
232
|
|
|
} |
233
|
|
|
|
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.