1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace kalanis\nested_tree\Sources\PDO; |
4
|
|
|
|
5
|
|
|
use kalanis\nested_tree\Support; |
6
|
|
|
use PDO as base_pdo; |
7
|
|
|
|
8
|
|
|
/** |
9
|
|
|
* The default SQL implementation |
10
|
|
|
*/ |
11
|
|
|
class MySql extends PDO |
12
|
|
|
{ |
13
|
5 |
|
public function selectLastPosition(?int $parentNodeId, ?Support\Conditions $where = null) : ?int |
14
|
|
|
{ |
15
|
5 |
|
$sql = 'SELECT `' . $this->settings->idColumnName . '`, `' . $this->settings->parentIdColumnName . '`, `' . $this->settings->positionColumnName . '`' |
16
|
5 |
|
. ' FROM `' . $this->settings->tableName . '`' |
17
|
5 |
|
. ' WHERE `' . $this->settings->parentIdColumnName . '` = :filter_parent_id'; |
18
|
5 |
|
$sql .= $this->addCustomQuery($where, ''); |
19
|
5 |
|
$sql .= ' ORDER BY `' . $this->settings->positionColumnName . '` DESC'; |
20
|
|
|
|
21
|
5 |
|
$Sth = $this->pdo->prepare($sql); |
22
|
5 |
|
$this->bindParentId($parentNodeId, $Sth); |
23
|
5 |
|
$this->bindCustomQuery($where, $Sth); |
24
|
|
|
|
25
|
5 |
|
$Sth->execute(); |
26
|
|
|
/** @var array<string|int, mixed>|false $row */ |
27
|
5 |
|
$row = $Sth->fetch(); |
28
|
5 |
|
$Sth->closeCursor(); |
29
|
|
|
|
30
|
5 |
|
if (!empty($row)) { |
31
|
5 |
|
return is_null($row[$this->settings->positionColumnName]) ? null : max(1, intval($row[$this->settings->positionColumnName])); |
32
|
|
|
} |
33
|
|
|
|
34
|
1 |
|
return null; |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* {@inheritdoc} |
39
|
|
|
*/ |
40
|
62 |
|
public function selectSimple(Support\Options $options) : array |
41
|
|
|
{ |
42
|
62 |
|
$sql = 'SELECT node.`' . $this->settings->idColumnName . '`' |
43
|
62 |
|
. ', node.`' . $this->settings->parentIdColumnName . '`' |
44
|
62 |
|
. ', node.`' . $this->settings->leftColumnName . '`' |
45
|
62 |
|
. ', node.`' . $this->settings->rightColumnName . '`' |
46
|
62 |
|
. ', node.`' . $this->settings->levelColumnName . '`' |
47
|
62 |
|
. ', node.`' . $this->settings->positionColumnName . '`' |
48
|
62 |
|
; |
49
|
62 |
|
$sql .= $this->addAdditionalColumns($options, 'node.'); |
50
|
62 |
|
$sql .= ' FROM `' . $this->settings->tableName . '` node'; |
51
|
62 |
|
$sql .= ' WHERE 1'; |
52
|
62 |
|
$sql .= $this->addCurrentId($options, 'node.'); |
53
|
62 |
|
$sql .= $this->addCustomQuery($options->where, 'node.'); |
54
|
62 |
|
$sql .= ' ORDER BY node.`' . $this->settings->positionColumnName . '` ASC'; |
55
|
|
|
|
56
|
62 |
|
$Sth = $this->pdo->prepare($sql); |
57
|
62 |
|
$this->bindCurrentId($options->currentId, $Sth); |
58
|
62 |
|
$this->bindCustomQuery($options->where, $Sth); |
59
|
|
|
|
60
|
62 |
|
$Sth->execute(); |
61
|
62 |
|
$result = $Sth->fetchAll(); |
62
|
|
|
|
63
|
62 |
|
return $result ? $this->fromDbRows($result) : []; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* {@inheritdoc} |
68
|
|
|
*/ |
69
|
9 |
|
public function selectParent(int $nodeId, Support\Options $options) : ?int |
70
|
|
|
{ |
71
|
9 |
|
$sql = 'SELECT node.`' . $this->settings->idColumnName . '`' |
72
|
9 |
|
. ', node.`' . $this->settings->parentIdColumnName . '`' |
73
|
9 |
|
. ', node.`' . $this->settings->leftColumnName . '`' |
74
|
9 |
|
. ', node.`' . $this->settings->rightColumnName . '`' |
75
|
9 |
|
. ', node.`' . $this->settings->levelColumnName . '`' |
76
|
9 |
|
. ', node.`' . $this->settings->positionColumnName . '`' |
77
|
9 |
|
; |
78
|
9 |
|
$sql .= $this->addAdditionalColumns($options, 'node.'); |
79
|
9 |
|
$sql .= ' FROM `' . $this->settings->tableName . '` node'; |
80
|
9 |
|
$sql .= ' WHERE node.`' . $this->settings->idColumnName . '` = :filter_taxonomy_id'; |
81
|
9 |
|
$sql .= $this->addCustomQuery($options->where, 'node.'); |
82
|
|
|
|
83
|
9 |
|
$Sth = $this->pdo->prepare($sql); |
84
|
9 |
|
$this->bindCurrentId($nodeId, $Sth); |
85
|
9 |
|
$this->bindCustomQuery($options->where, $Sth); |
86
|
|
|
|
87
|
9 |
|
$Sth->execute(); |
88
|
|
|
/** @var array<string|int, mixed>|false $row */ |
89
|
9 |
|
$row = $Sth->fetch(); |
90
|
9 |
|
$parent_id = $row ? $row[$this->settings->parentIdColumnName] : null; |
91
|
9 |
|
$Sth->closeCursor(); |
92
|
|
|
|
93
|
9 |
|
return (empty($parent_id)) ? ($this->settings->rootIsNull ? null : 0) : max(0, intval($parent_id)); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* {@inheritdoc} |
98
|
|
|
*/ |
99
|
41 |
|
public function selectCount(Support\Options $options) : int |
100
|
|
|
{ |
101
|
41 |
|
$sql = 'SELECT '; |
102
|
41 |
|
$sql .= ' ANY_VALUE(`parent`.`' . $this->settings->idColumnName . '`)'; |
103
|
41 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->parentIdColumnName . '`)'; |
104
|
41 |
|
if (!is_null($options->currentId) || !is_null($options->parentId) || !empty($options->search) || $options->joinChild) { |
105
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->idColumnName . '`) AS `' . $this->settings->idColumnName . '`'; |
106
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->parentIdColumnName . '`) AS `' . $this->settings->parentIdColumnName . '`'; |
107
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->leftColumnName . '`) AS `' . $this->settings->leftColumnName . '`'; |
108
|
|
|
} |
109
|
41 |
|
$sql .= $this->addAdditionalColumns($options); |
110
|
41 |
|
$sql .= ' FROM `' . $this->settings->tableName . '` AS `parent`'; |
111
|
|
|
|
112
|
41 |
|
if (!is_null($options->currentId) || !is_null($options->parentId) || !empty($options->search) || $options->joinChild) { |
113
|
|
|
// if there is filter or search, there must be inner join to select all of filtered children. |
114
|
35 |
|
$sql .= ' INNER JOIN `' . $this->settings->tableName . '` AS `child`'; |
115
|
35 |
|
$sql .= ' ON `child`.`' . $this->settings->leftColumnName . '` BETWEEN `parent`.`' . $this->settings->leftColumnName . '` AND `parent`.`' . $this->settings->rightColumnName . '`'; |
116
|
|
|
} |
117
|
|
|
|
118
|
41 |
|
$sql .= ' WHERE 1'; |
119
|
41 |
|
$sql .= $this->addFilterBy($options); |
120
|
41 |
|
$sql .= $this->addCurrentId($options, '`parent`.'); |
121
|
41 |
|
$sql .= $this->addParentId($options, '`parent`.'); |
122
|
41 |
|
$sql .= $this->addSearch($options, '`parent`.'); |
123
|
41 |
|
$sql .= $this->addCustomQuery($options->where); |
124
|
41 |
|
$sql .= $this->addSorting($options); |
125
|
|
|
|
126
|
|
|
// prepare and get 'total' count. |
127
|
41 |
|
$Sth = $this->pdo->prepare($sql); |
128
|
41 |
|
$this->bindCurrentId($options->currentId, $Sth); |
129
|
41 |
|
$this->bindParentId($options->parentId, $Sth, true); |
130
|
41 |
|
$this->bindSearch($options, $Sth); |
131
|
41 |
|
$this->bindCustomQuery($options->where, $Sth); |
132
|
|
|
|
133
|
41 |
|
$Sth->execute(); |
134
|
41 |
|
$result = $Sth->fetchAll(); |
135
|
|
|
|
136
|
|
|
// "a bit" hardcore - get all lines and then count them |
137
|
41 |
|
return $result ? count($result) : 0; |
138
|
|
|
} |
139
|
|
|
|
140
|
41 |
|
public function selectLimited(Support\Options $options) : array |
141
|
|
|
{ |
142
|
41 |
|
$sql = 'SELECT'; |
143
|
41 |
|
$sql .= ' ANY_VALUE(`parent`.`' . $this->settings->idColumnName . '`)'; |
144
|
41 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->parentIdColumnName . '`)'; |
145
|
41 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->leftColumnName . '`)'; |
146
|
41 |
|
if (!is_null($options->currentId) || !is_null($options->parentId) || !empty($options->search) || $options->joinChild) { |
147
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->idColumnName . '`) AS `' . $this->settings->idColumnName . '`'; |
148
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->parentIdColumnName . '`) AS `' . $this->settings->parentIdColumnName . '`'; |
149
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->leftColumnName . '`) AS `' . $this->settings->leftColumnName . '`'; |
150
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->rightColumnName . '`) AS `' . $this->settings->rightColumnName . '`'; |
151
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->levelColumnName . '`) AS `' . $this->settings->levelColumnName . '`'; |
152
|
35 |
|
$sql .= ', ANY_VALUE(`child`.`' . $this->settings->positionColumnName . '`) AS `' . $this->settings->positionColumnName . '`'; |
153
|
|
|
} |
154
|
41 |
|
$sql .= $this->addAdditionalColumns($options); |
155
|
41 |
|
$sql .= ' FROM `' . $this->settings->tableName . '` AS `parent`'; |
156
|
|
|
|
157
|
41 |
|
if (!is_null($options->currentId) || !is_null($options->parentId) || !empty($options->search) || $options->joinChild) { |
158
|
|
|
// if there is filter or search, there must be inner join to select all of filtered children. |
159
|
35 |
|
$sql .= ' INNER JOIN `' . $this->settings->tableName . '` AS `child`'; |
160
|
35 |
|
$sql .= ' ON `child`.`' . $this->settings->leftColumnName . '` BETWEEN `parent`.`' . $this->settings->leftColumnName . '` AND `parent`.`' . $this->settings->rightColumnName . '`'; |
161
|
|
|
} |
162
|
|
|
|
163
|
41 |
|
$sql .= ' WHERE 1'; |
164
|
41 |
|
$sql .= $this->addFilterBy($options); |
165
|
41 |
|
$sql .= $this->addCurrentId($options, '`parent`.'); |
166
|
41 |
|
$sql .= $this->addParentId($options, '`parent`.'); |
167
|
41 |
|
$sql .= $this->addSearch($options, '`parent`.'); |
168
|
41 |
|
$sql .= $this->addCustomQuery($options->where); |
169
|
41 |
|
$sql .= $this->addSorting($options); |
170
|
|
|
|
171
|
|
|
// re-create query and prepare. second step is for set limit and fetch all items. |
172
|
41 |
|
if (!$options->unlimited) { |
173
|
10 |
|
if (empty($options->offset)) { |
174
|
10 |
|
$options->offset = 0; |
175
|
|
|
} |
176
|
10 |
|
if (empty($options->limit) || (10000 < $options->limit)) { |
177
|
10 |
|
$options->limit = 20; |
178
|
|
|
} |
179
|
|
|
|
180
|
10 |
|
$sql .= ' LIMIT ' . $options->offset . ', ' . $options->limit; |
181
|
|
|
} |
182
|
|
|
|
183
|
41 |
|
$Sth = $this->pdo->prepare($sql); |
184
|
41 |
|
$this->bindCurrentId($options->currentId, $Sth); |
185
|
41 |
|
$this->bindParentId($options->parentId, $Sth, true); |
186
|
41 |
|
$this->bindSearch($options, $Sth); |
187
|
41 |
|
$this->bindCustomQuery($options->where, $Sth); |
188
|
|
|
|
189
|
41 |
|
$Sth->execute(); |
190
|
41 |
|
$result = $Sth->fetchAll(); |
191
|
|
|
|
192
|
41 |
|
return $result ? $this->fromDbRows($result) : []; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* {@inheritdoc} |
197
|
|
|
*/ |
198
|
12 |
|
public function selectWithParents(Support\Options $options) : array |
199
|
|
|
{ |
200
|
12 |
|
$sql = 'SELECT'; |
201
|
12 |
|
$sql .= ' ANY_VALUE(`parent`.`' . $this->settings->idColumnName . '`) AS `' . $this->settings->idColumnName . '`'; |
202
|
12 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->parentIdColumnName . '`) AS `' . $this->settings->parentIdColumnName . '`'; |
203
|
12 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->leftColumnName . '`) AS `' . $this->settings->leftColumnName . '`'; |
204
|
12 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->rightColumnName . '`) AS `' . $this->settings->rightColumnName . '`'; |
205
|
12 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->levelColumnName . '`) AS `' . $this->settings->levelColumnName . '`'; |
206
|
12 |
|
$sql .= ', ANY_VALUE(`parent`.`' . $this->settings->positionColumnName . '`) AS `' . $this->settings->positionColumnName . '`'; |
207
|
12 |
|
$sql .= $this->addAdditionalColumns($options); |
208
|
12 |
|
$sql .= ' FROM `' . $this->settings->tableName . '` AS `node`,'; |
209
|
12 |
|
$sql .= ' `' . $this->settings->tableName . '` AS `parent`'; |
210
|
12 |
|
$sql .= ' WHERE'; |
211
|
12 |
|
$sql .= ' (`node`.`' . $this->settings->leftColumnName . '` BETWEEN `parent`.`' . $this->settings->leftColumnName . '` AND `parent`.`' . $this->settings->rightColumnName . '`)'; |
212
|
12 |
|
$sql .= $this->addCurrentId($options, '`node`.'); |
213
|
12 |
|
$sql .= $this->addSearch($options, '`node`.'); |
214
|
12 |
|
$sql .= $this->addCustomQuery($options->where); |
215
|
12 |
|
$sql .= ' GROUP BY `parent`.`' . $this->settings->idColumnName . '`'; |
216
|
12 |
|
$sql .= ' ORDER BY `parent`.`' . $this->settings->leftColumnName . '`'; |
217
|
|
|
|
218
|
12 |
|
$Sth = $this->pdo->prepare($sql); |
219
|
12 |
|
$this->bindCurrentId($options->currentId, $Sth); |
220
|
12 |
|
$this->bindSearch($options, $Sth); |
221
|
12 |
|
$this->bindCustomQuery($options->where, $Sth); |
222
|
|
|
|
223
|
12 |
|
$Sth->execute(); |
224
|
12 |
|
$result = $Sth->fetchAll(); |
225
|
12 |
|
$Sth->closeCursor(); |
226
|
|
|
|
227
|
12 |
|
if (empty($result)) { |
228
|
1 |
|
return []; |
229
|
|
|
} |
230
|
12 |
|
if ($options->skipCurrent) { |
231
|
2 |
|
unset($result[count($result)-1]); |
232
|
|
|
} |
233
|
|
|
|
234
|
12 |
|
return $this->fromDbRows($result); |
235
|
|
|
} |
236
|
|
|
|
237
|
1 |
|
public function add(Support\Node $node, ?Support\Conditions $where = null) : Support\Node |
238
|
|
|
{ |
239
|
|
|
// Insert itself |
240
|
1 |
|
$sql = 'INSERT INTO `' . $this->settings->tableName . '`'; |
241
|
1 |
|
$lookup = []; |
242
|
1 |
|
$pairs = []; |
243
|
1 |
|
foreach ((array) $node as $column => $value) { |
244
|
1 |
|
if (!is_numeric($column) && !$this->isColumnNameFromBasic($column)) { |
245
|
1 |
|
$translateColumn = $this->translateColumn($this->settings, $column); |
246
|
1 |
|
$lookup['`' . $translateColumn . '`'] = ':' . $translateColumn; |
247
|
1 |
|
$pairs[':' . $translateColumn] = $value; |
248
|
|
|
} |
249
|
|
|
} |
250
|
1 |
|
$sql .= '(' . implode(',', array_keys($lookup)) . ')'; |
251
|
1 |
|
$sql .= 'VALUES (' . implode(',', array_keys($pairs)) . ')'; |
252
|
|
|
|
253
|
1 |
|
$Sth = $this->pdo->prepare($sql); |
254
|
|
|
|
255
|
1 |
|
foreach ($pairs as $column => $value) { |
256
|
1 |
|
$Sth->bindValue($column, $value); |
257
|
|
|
} |
258
|
|
|
|
259
|
1 |
|
$execute = $Sth->execute(); |
260
|
1 |
|
if (!$execute) { |
261
|
|
|
// @codeCoverageIgnoreStart |
262
|
|
|
// when this happens it is problem with DB, not with library |
263
|
|
|
throw new \RuntimeException('Cannot save!'); |
264
|
|
|
} |
265
|
|
|
// @codeCoverageIgnoreEnd |
266
|
1 |
|
$Sth->closeCursor(); |
267
|
|
|
|
268
|
|
|
// Now get it back |
269
|
1 |
|
$sql = 'SELECT * FROM `' . $this->settings->tableName . '` WHERE 1'; |
270
|
1 |
|
foreach ($lookup as $column => $bind) { |
271
|
1 |
|
$sql .= ' AND ' . $column . ' = ' . $bind; |
272
|
|
|
} |
273
|
1 |
|
$Lth = $this->pdo->prepare($sql); |
274
|
1 |
|
foreach ($pairs as $column => $value) { |
275
|
1 |
|
$Lth->bindValue($column, $value); |
276
|
|
|
} |
277
|
|
|
|
278
|
1 |
|
$Lth->execute(); |
279
|
|
|
/** @var array<string|int, mixed>|false $row */ |
280
|
1 |
|
$row = $Lth->fetch(); |
281
|
1 |
|
$node = $row ? $this->fillDataFromRow($row) : null; |
282
|
1 |
|
$Lth->closeCursor(); |
283
|
|
|
|
284
|
1 |
|
if (is_null($node)) { |
285
|
|
|
// @codeCoverageIgnoreStart |
286
|
|
|
// when this happens it is problem with DB, not with library |
287
|
|
|
throw new \RuntimeException('Node not found in database'); |
288
|
|
|
} |
289
|
|
|
// @codeCoverageIgnoreEnd |
290
|
|
|
|
291
|
1 |
|
return $node; |
292
|
|
|
} |
293
|
|
|
|
294
|
1 |
|
public function updateData(Support\Node $node, ?Support\Conditions $where = null) : bool |
295
|
|
|
{ |
296
|
1 |
|
$sql = 'UPDATE `' . $this->settings->tableName . '`'; |
297
|
1 |
|
$sql .= ' SET'; |
298
|
1 |
|
$pairs = []; |
299
|
1 |
|
$lookup = []; |
300
|
1 |
|
foreach ((array) $node as $column => $value) { |
301
|
|
|
if ( |
302
|
1 |
|
!is_numeric($column) |
303
|
1 |
|
&& !$this->isColumnNameFromBasic($column) |
304
|
1 |
|
&& !$this->isColumnNameFromTree($column) |
305
|
1 |
|
&& !is_null($value) |
306
|
|
|
) { |
307
|
1 |
|
$translateColumn = $this->translateColumn($this->settings, $column); |
308
|
1 |
|
$lookup[] = '`' . $translateColumn . '` = :' . $translateColumn; |
309
|
1 |
|
$pairs[':' . $translateColumn] = $value; |
310
|
|
|
} |
311
|
|
|
} |
312
|
1 |
|
$sql .= implode(',', $lookup); |
313
|
1 |
|
$sql .= ' WHERE `' . $this->settings->idColumnName . '` = :id'; |
314
|
|
|
|
315
|
1 |
|
$sql .= $this->addCustomQuery($where, ''); |
316
|
1 |
|
$Sth = $this->pdo->prepare($sql); |
317
|
|
|
|
318
|
1 |
|
$Sth->bindValue(':id', $node->id, base_pdo::PARAM_INT); |
319
|
1 |
|
foreach ($pairs as $column => $value) { |
320
|
1 |
|
$Sth->bindValue($column, $value); |
321
|
|
|
} |
322
|
1 |
|
$this->bindCustomQuery($where, $Sth); |
323
|
|
|
|
324
|
1 |
|
$execute = $Sth->execute(); |
325
|
1 |
|
$Sth->closeCursor(); |
326
|
|
|
|
327
|
1 |
|
return $execute; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* {@inheritdoc} |
332
|
|
|
*/ |
333
|
4 |
|
public function updateNodeParent(int $nodeId, ?int $parentId, int $position, ?Support\Conditions $where = null) : bool |
334
|
|
|
{ |
335
|
4 |
|
$sql = 'UPDATE `' . $this->settings->tableName . '`'; |
336
|
4 |
|
$sql .= ' SET `' . $this->settings->parentIdColumnName . '` = :filter_parent_id'; |
337
|
4 |
|
$sql .= ' , `' . $this->settings->positionColumnName . '` = :position'; |
338
|
4 |
|
$sql .= ' WHERE `' . $this->settings->idColumnName . '` = :filter_taxonomy_id'; |
339
|
4 |
|
$sql .= $this->addCustomQuery($where, ''); |
340
|
|
|
|
341
|
4 |
|
$Sth = $this->pdo->prepare($sql); |
342
|
4 |
|
$this->bindParentId($parentId, $Sth); |
343
|
4 |
|
$Sth->bindValue(':position', $position, base_pdo::PARAM_INT); |
344
|
4 |
|
$this->bindCurrentId($nodeId, $Sth); |
345
|
4 |
|
$this->bindCustomQuery($where, $Sth); |
346
|
|
|
|
347
|
4 |
|
$execute = $Sth->execute(); |
348
|
4 |
|
$Sth->closeCursor(); |
349
|
|
|
|
350
|
4 |
|
return $execute; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* {@inheritdoc} |
355
|
|
|
*/ |
356
|
5 |
|
public function updateChildrenParent(int $nodeId, ?int $parentId, ?Support\Conditions $where = null) : bool |
357
|
|
|
{ |
358
|
5 |
|
$sql = 'UPDATE `' . $this->settings->tableName . '`'; |
359
|
5 |
|
$sql .= ' SET `' . $this->settings->parentIdColumnName . '` = :filter_parent_id'; |
360
|
5 |
|
$sql .= ' WHERE `' . $this->settings->parentIdColumnName . '` = :filter_taxonomy_id'; |
361
|
5 |
|
$sql .= $this->addCustomQuery($where, ''); |
362
|
|
|
|
363
|
5 |
|
$Sth = $this->pdo->prepare($sql); |
364
|
5 |
|
$this->bindParentId($parentId, $Sth); |
365
|
5 |
|
$this->bindCurrentId($nodeId, $Sth); |
366
|
5 |
|
$this->bindCustomQuery($where, $Sth); |
367
|
|
|
|
368
|
5 |
|
$execute = $Sth->execute(); |
369
|
5 |
|
$Sth->closeCursor(); |
370
|
|
|
|
371
|
5 |
|
return $execute; |
372
|
|
|
} |
373
|
|
|
|
374
|
61 |
|
public function updateLeftRightPos(Support\Node $row, ?Support\Conditions $where = null) : bool |
375
|
|
|
{ |
376
|
61 |
|
$sql = 'UPDATE `' . $this->settings->tableName . '`'; |
377
|
61 |
|
$sql .= ' SET'; |
378
|
61 |
|
$sql .= ' `' . $this->settings->levelColumnName . '` = :level,'; |
379
|
61 |
|
$sql .= ' `' . $this->settings->leftColumnName . '` = :left,'; |
380
|
61 |
|
$sql .= ' `' . $this->settings->rightColumnName . '` = :right,'; |
381
|
61 |
|
$sql .= ' `' . $this->settings->positionColumnName . '` = :pos'; |
382
|
61 |
|
$sql .= ' WHERE `' . $this->settings->idColumnName . '` = :id'; |
383
|
61 |
|
$sql .= $this->addCustomQuery($where, ''); |
384
|
|
|
|
385
|
61 |
|
$Sth = $this->pdo->prepare($sql); |
386
|
61 |
|
$Sth->bindValue(':level', $row->level, base_pdo::PARAM_INT); |
387
|
61 |
|
$Sth->bindValue(':left', $row->left, base_pdo::PARAM_INT); |
388
|
61 |
|
$Sth->bindValue(':right', $row->right, base_pdo::PARAM_INT); |
389
|
61 |
|
$Sth->bindValue(':pos', $row->position, base_pdo::PARAM_INT); |
390
|
61 |
|
$Sth->bindValue(':id', $row->id, base_pdo::PARAM_INT); |
391
|
61 |
|
$this->bindCustomQuery($where, $Sth); |
392
|
|
|
|
393
|
61 |
|
$execute = $Sth->execute(); |
394
|
61 |
|
$Sth->closeCursor(); |
395
|
|
|
|
396
|
61 |
|
return $execute; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* {@inheritdoc} |
401
|
|
|
*/ |
402
|
3 |
|
public function makeHole(?int $parentId, int $position, bool $moveUp, ?Support\Conditions $where = null) : bool |
403
|
|
|
{ |
404
|
3 |
|
$direction = $moveUp ? '-' : '+'; |
405
|
3 |
|
$compare = $moveUp ? '<=' : '>='; |
406
|
3 |
|
$sql = 'UPDATE `' . $this->settings->tableName . '`'; |
407
|
3 |
|
$sql .= ' SET `' . $this->settings->positionColumnName . '` = `' . $this->settings->positionColumnName . '` ' . $direction . ' 1'; |
408
|
3 |
|
$sql .= ' WHERE `' . $this->settings->parentIdColumnName . '` = :filter_parent_id'; |
409
|
3 |
|
$sql .= ' AND `' . $this->settings->positionColumnName . '` ' . $compare . ' :position'; |
410
|
|
|
|
411
|
3 |
|
$sql .= $this->addCustomQuery($where, ''); |
412
|
3 |
|
$Sth = $this->pdo->prepare($sql); |
413
|
|
|
|
414
|
3 |
|
$Sth->bindValue(':position', $position, base_pdo::PARAM_INT); |
415
|
3 |
|
$this->bindParentId($parentId, $Sth); |
416
|
3 |
|
$this->bindCustomQuery($where, $Sth); |
417
|
|
|
|
418
|
3 |
|
$execute = $Sth->execute(); |
419
|
3 |
|
$Sth->closeCursor(); |
420
|
|
|
|
421
|
3 |
|
return $execute; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* {@inheritdoc} |
426
|
|
|
*/ |
427
|
5 |
|
public function deleteSolo(int $nodeId, ?Support\Conditions $where = null) : bool |
428
|
|
|
{ |
429
|
|
|
// delete the selected taxonomy ID |
430
|
5 |
|
$sql = 'DELETE FROM `' . $this->settings->tableName . '` WHERE `' . $this->settings->idColumnName . '` = :filter_taxonomy_id'; |
431
|
5 |
|
$sql .= $this->addCustomQuery($where, ''); |
432
|
5 |
|
$Sth = $this->pdo->prepare($sql); |
433
|
|
|
|
434
|
5 |
|
$this->bindCurrentId($nodeId, $Sth); |
435
|
5 |
|
$this->bindCustomQuery($where, $Sth); |
436
|
|
|
|
437
|
5 |
|
$execute = $Sth->execute(); |
438
|
5 |
|
$Sth->closeCursor(); |
439
|
|
|
|
440
|
5 |
|
return $execute; |
441
|
|
|
} |
442
|
|
|
|
443
|
3 |
|
public function deleteWithChildren(Support\Node $row, ?Support\Conditions $where = null) : bool |
444
|
|
|
{ |
445
|
3 |
|
$sql = 'DELETE FROM `' . $this->settings->tableName . '` WHERE `' . $this->settings->idColumnName . '` = :filter_taxonomy_id'; |
446
|
3 |
|
$sql .= $this->addCustomQuery($where, ''); |
447
|
3 |
|
$Sth = $this->pdo->prepare($sql); |
448
|
|
|
|
449
|
3 |
|
$this->bindCurrentId($row->id, $Sth); |
450
|
3 |
|
$this->bindCustomQuery($where, $Sth); |
451
|
|
|
|
452
|
3 |
|
$execute = $Sth->execute(); |
453
|
3 |
|
$Sth->closeCursor(); |
454
|
|
|
|
455
|
3 |
|
return $execute; |
456
|
|
|
} |
457
|
|
|
|
458
|
19 |
|
protected function replaceColumns(string $query, string $byWhat = '') : string |
459
|
|
|
{ |
460
|
19 |
|
foreach (['`parent`.', '`child`.', 'parent.', 'child.'] as $toReplace) { |
461
|
19 |
|
$query = str_replace($toReplace, $byWhat, $query); |
462
|
|
|
} |
463
|
|
|
|
464
|
19 |
|
return $query; |
465
|
|
|
} |
466
|
|
|
|
467
|
62 |
|
protected function addAdditionalColumns(Support\Options $options, ?string $replaceName = null) : string |
468
|
|
|
{ |
469
|
62 |
|
$sql = ''; |
470
|
62 |
|
if (!empty($options->additionalColumns)) { |
471
|
41 |
|
foreach ($options->additionalColumns as $column) { |
472
|
41 |
|
$sql .= ', ' . (!is_null($replaceName) ? $this->replaceColumns($column, $replaceName) : $column); |
473
|
|
|
} |
474
|
|
|
} |
475
|
|
|
|
476
|
62 |
|
return $sql; |
477
|
|
|
} |
478
|
|
|
|
479
|
41 |
|
protected function addFilterBy(Support\Options $options) : string |
480
|
|
|
{ |
481
|
41 |
|
$sql = ''; |
482
|
41 |
|
if (!empty($options->filterIdBy)) { |
483
|
|
|
// Due to IN() and NOT IN() cannot using bindValue directly. |
484
|
|
|
// read more at http://stackoverflow.com/questions/17746667/php-pdo-for-not-in-query-in-mysql |
485
|
|
|
// and http://stackoverflow.com/questions/920353/can-i-bind-an-array-to-an-in-condition |
486
|
|
|
// it is possible to go around that, but it needs a bit more tinkering |
487
|
|
|
|
488
|
|
|
// loop remove non-number for safety. |
489
|
4 |
|
foreach ($options->filterIdBy as $key => $eachNodeId) { |
490
|
4 |
|
if (!is_numeric($eachNodeId) || intval($eachNodeId) !== intval($eachNodeId)) { |
491
|
2 |
|
unset($options->filterIdBy[$key]); |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
// build value for use with `IN()` function. Example: 1,3,4,5. |
496
|
4 |
|
$nodeIdIn = implode(',', $options->filterIdBy); |
497
|
4 |
|
$sql .= ' AND `parent`.`' . $this->settings->idColumnName . '` IN (' . $nodeIdIn . ')'; |
498
|
|
|
} |
499
|
|
|
|
500
|
41 |
|
return $sql; |
501
|
|
|
} |
502
|
|
|
|
503
|
62 |
|
protected function addCurrentId(Support\Options $options, string $dbPrefix = '') : string |
504
|
|
|
{ |
505
|
62 |
|
$sql = ''; |
506
|
62 |
|
if (!is_null($options->currentId)) { |
507
|
24 |
|
$sql .= ' AND ' . $dbPrefix . '`' . $this->settings->idColumnName . '` = :filter_taxonomy_id'; |
508
|
|
|
} |
509
|
|
|
|
510
|
62 |
|
return $sql; |
511
|
|
|
} |
512
|
|
|
|
513
|
41 |
|
protected function addParentId(Support\Options $options, string $dbPrefix = '') : string |
514
|
|
|
{ |
515
|
41 |
|
$sql = ''; |
516
|
41 |
|
if (!is_null($options->parentId)) { |
517
|
2 |
|
$sql .= ' AND ' . $dbPrefix . '`' . $this->settings->parentIdColumnName . '` = :filter_parent_id'; |
518
|
|
|
} |
519
|
|
|
|
520
|
41 |
|
return $sql; |
521
|
|
|
} |
522
|
|
|
|
523
|
62 |
|
protected function bindCurrentId(?int $currentId, \PDOStatement $pdo) : void |
524
|
|
|
{ |
525
|
62 |
|
if (!is_null($currentId)) { |
526
|
29 |
|
$pdo->bindValue(':filter_taxonomy_id', $currentId, base_pdo::PARAM_INT); |
527
|
|
|
} |
528
|
|
|
} |
529
|
|
|
|
530
|
46 |
|
protected function bindParentId(?int $parentId, \PDOStatement $pdo, bool $skipNull = false) : void |
531
|
|
|
{ |
532
|
46 |
|
if (is_null($parentId) && !$skipNull) { |
533
|
1 |
|
$pdo->bindValue(':filter_parent_id', null, base_pdo::PARAM_NULL); |
534
|
46 |
|
} elseif (!is_null($parentId)) { |
535
|
15 |
|
$pdo->bindValue(':filter_parent_id', $parentId, base_pdo::PARAM_INT); |
536
|
|
|
} |
537
|
|
|
} |
538
|
|
|
|
539
|
52 |
|
protected function addSearch(Support\Options $options, string $dbPrefix = '') : string |
540
|
|
|
{ |
541
|
52 |
|
$sql = ''; |
542
|
|
|
if ( |
543
|
52 |
|
!empty($options->search->columns) |
544
|
52 |
|
&& !empty($options->search->value) |
545
|
|
|
) { |
546
|
5 |
|
$sql .= ' AND ('; |
547
|
5 |
|
$array_keys = array_keys($options->search->columns); |
548
|
5 |
|
$last_array_key = array_pop($array_keys); |
549
|
5 |
|
foreach ($options->search->columns as $key => $column) { |
550
|
5 |
|
$sql .= $dbPrefix . '`' . $column . '` LIKE :search'; |
551
|
5 |
|
if ($key !== $last_array_key) { |
552
|
1 |
|
$sql .= ' OR '; |
553
|
|
|
} |
554
|
|
|
} |
555
|
5 |
|
$sql .= ')'; |
556
|
|
|
} |
557
|
|
|
|
558
|
52 |
|
return $sql; |
559
|
|
|
} |
560
|
|
|
|
561
|
52 |
|
protected function bindSearch(Support\Options $options, \PDOStatement $pdo) : void |
562
|
|
|
{ |
563
|
52 |
|
if (!empty($options->search->value)) { |
564
|
5 |
|
$pdo->bindValue(':search', '%' . $options->search->value . '%'); |
565
|
|
|
} |
566
|
|
|
} |
567
|
|
|
|
568
|
65 |
|
protected function addCustomQuery(?Support\Conditions $where, ?string $replaceName = null) : string |
569
|
|
|
{ |
570
|
65 |
|
$sql = ''; |
571
|
65 |
|
if (!empty($where->query)) { |
572
|
22 |
|
$sql .= ' AND ' . (!is_null($replaceName) ? $this->replaceColumns($where->query, $replaceName) : $where->query); |
573
|
|
|
} |
574
|
|
|
|
575
|
65 |
|
return $sql; |
576
|
|
|
} |
577
|
|
|
|
578
|
65 |
|
protected function bindCustomQuery(?Support\Conditions $where, \PDOStatement $pdo) : void |
579
|
|
|
{ |
580
|
65 |
|
if (!empty($where->bindValues)) { |
581
|
22 |
|
foreach ($where->bindValues as $bindName => $bindValue) { |
582
|
22 |
|
$pdo->bindValue($bindName, $bindValue); |
583
|
|
|
} |
584
|
|
|
} |
585
|
|
|
} |
586
|
|
|
|
587
|
41 |
|
protected function addSorting(Support\Options $options) : string |
588
|
|
|
{ |
589
|
41 |
|
$sql = ''; |
590
|
41 |
|
if (!$options->noSortOrder) { |
591
|
39 |
|
if (!is_null($options->currentId) || !is_null($options->parentId) || !empty($options->search) || $options->joinChild) { |
592
|
33 |
|
$sql .= ' GROUP BY `child`.`' . $this->settings->idColumnName . '`'; |
593
|
33 |
|
$order_by = '`child`.`' . $this->settings->leftColumnName . '` ASC'; |
594
|
10 |
|
} elseif (!empty($options->filterIdBy)) { |
595
|
1 |
|
$nodeIdIn = implode(',', $options->filterIdBy); |
596
|
1 |
|
$order_by = 'FIELD(`' . $this->settings->idColumnName . '`,' . $nodeIdIn . ')'; |
597
|
|
|
} else { |
598
|
9 |
|
$order_by = '`parent`.`' . $this->settings->leftColumnName . '` ASC'; |
599
|
|
|
} |
600
|
39 |
|
$sql .= ' ORDER BY ' . $order_by; |
601
|
2 |
|
} elseif ($options->joinChild) { |
602
|
2 |
|
$sql .= ' GROUP BY `' . $this->settings->idColumnName . '`'; |
603
|
|
|
} |
604
|
|
|
|
605
|
41 |
|
return $sql; |
606
|
|
|
} |
607
|
|
|
} |
608
|
|
|
|