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