1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace kalanis\kw_mapper\Search\Connector; |
4
|
|
|
|
5
|
|
|
|
6
|
|
|
use kalanis\kw_mapper\Interfaces\IQueryBuilder; |
7
|
|
|
use kalanis\kw_mapper\MapperException; |
8
|
|
|
use kalanis\kw_mapper\Mappers\Shared\ForeignKey; |
9
|
|
|
use kalanis\kw_mapper\Records\ARecord; |
10
|
|
|
use kalanis\kw_mapper\Storage; |
11
|
|
|
|
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Class AConnector |
15
|
|
|
* @package kalanis\kw_mapper\Search |
16
|
|
|
* Connect real sources into search engine |
17
|
|
|
*/ |
18
|
|
|
abstract class AConnector |
19
|
|
|
{ |
20
|
|
|
use Database\TRecordsInJoins; |
21
|
|
|
|
22
|
|
|
protected ARecord $basicRecord; |
23
|
|
|
protected Storage\Shared\QueryBuilder $queryBuilder; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @param string $table |
27
|
|
|
* @param string $column |
28
|
|
|
* @param string|float|int $value |
29
|
|
|
* @throws MapperException |
30
|
|
|
* @return $this |
31
|
|
|
*/ |
32
|
1 |
|
public function notExact(string $table, string $column, $value): self |
33
|
|
|
{ |
34
|
1 |
|
$aTable = $this->correctTable($table); |
35
|
1 |
|
$this->getQueryBuilder()->addCondition( |
36
|
1 |
|
$aTable, |
37
|
1 |
|
$this->correctColumn($aTable, $column), |
38
|
1 |
|
IQueryBuilder::OPERATION_NEQ, |
39
|
1 |
|
$value |
40
|
1 |
|
); |
41
|
1 |
|
return $this; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @param string $table |
46
|
|
|
* @param string $column |
47
|
|
|
* @param string|float|int $value |
48
|
|
|
* @throws MapperException |
49
|
|
|
* @return $this |
50
|
|
|
*/ |
51
|
8 |
|
public function exact(string $table, string $column, $value): self |
52
|
|
|
{ |
53
|
8 |
|
$aTable = $this->correctTable($table); |
54
|
8 |
|
$this->getQueryBuilder()->addCondition( |
55
|
8 |
|
$aTable, |
56
|
8 |
|
$this->correctColumn($aTable, $column), |
57
|
8 |
|
IQueryBuilder::OPERATION_EQ, |
58
|
8 |
|
$value |
59
|
8 |
|
); |
60
|
7 |
|
return $this; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @param string $table |
65
|
|
|
* @param string $column |
66
|
|
|
* @param string|float|int $value |
67
|
|
|
* @param bool $equals |
68
|
|
|
* @throws MapperException |
69
|
|
|
* @return $this |
70
|
|
|
*/ |
71
|
1 |
|
public function from(string $table, string $column, $value, bool $equals = true): self |
72
|
|
|
{ |
73
|
1 |
|
$aTable = $this->correctTable($table); |
74
|
1 |
|
$this->getQueryBuilder()->addCondition( |
75
|
1 |
|
$aTable, |
76
|
1 |
|
$this->correctColumn($aTable, $column), |
77
|
1 |
|
$equals ? IQueryBuilder::OPERATION_GTE : IQueryBuilder::OPERATION_GT, |
78
|
1 |
|
$value |
79
|
1 |
|
); |
80
|
1 |
|
return $this; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param string $table |
85
|
|
|
* @param string $column |
86
|
|
|
* @param string|float|int $value |
87
|
|
|
* @param bool $equals |
88
|
|
|
* @throws MapperException |
89
|
|
|
* @return $this |
90
|
|
|
*/ |
91
|
1 |
|
public function to(string $table, string $column, $value, bool $equals = true): self |
92
|
|
|
{ |
93
|
1 |
|
$aTable = $this->correctTable($table); |
94
|
1 |
|
$this->getQueryBuilder()->addCondition( |
95
|
1 |
|
$aTable, |
96
|
1 |
|
$this->correctColumn($aTable, $column), |
97
|
1 |
|
$equals ? IQueryBuilder::OPERATION_LTE : IQueryBuilder::OPERATION_LT, |
98
|
1 |
|
$value |
99
|
1 |
|
); |
100
|
1 |
|
return $this; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @param string $table |
105
|
|
|
* @param string $column |
106
|
|
|
* @param string $value |
107
|
|
|
* @throws MapperException |
108
|
|
|
* @return $this |
109
|
|
|
*/ |
110
|
7 |
|
public function like(string $table, string $column, $value): self |
111
|
|
|
{ |
112
|
7 |
|
$aTable = $this->correctTable($table); |
113
|
7 |
|
$this->getQueryBuilder()->addCondition( |
114
|
7 |
|
$aTable, |
115
|
7 |
|
$this->correctColumn($aTable, $column), |
116
|
7 |
|
IQueryBuilder::OPERATION_LIKE, |
117
|
7 |
|
$value |
118
|
7 |
|
); |
119
|
6 |
|
return $this; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @param string $table |
124
|
|
|
* @param string $column |
125
|
|
|
* @param string $value |
126
|
|
|
* @throws MapperException |
127
|
|
|
* @return $this |
128
|
|
|
*/ |
129
|
2 |
|
public function notLike(string $table, string $column, $value): self |
130
|
|
|
{ |
131
|
2 |
|
$aTable = $this->correctTable($table); |
132
|
2 |
|
$this->getQueryBuilder()->addCondition( |
133
|
2 |
|
$aTable, |
134
|
2 |
|
$this->correctColumn($aTable, $column), |
135
|
2 |
|
IQueryBuilder::OPERATION_NLIKE, |
136
|
2 |
|
$value |
137
|
2 |
|
); |
138
|
2 |
|
return $this; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @param string $table |
143
|
|
|
* @param string $column |
144
|
|
|
* @param string $pattern |
145
|
|
|
* @throws MapperException |
146
|
|
|
* @return $this |
147
|
|
|
*/ |
148
|
1 |
|
public function regexp(string $table, string $column, string $pattern): self |
149
|
|
|
{ |
150
|
1 |
|
$aTable = $this->correctTable($table); |
151
|
1 |
|
$this->getQueryBuilder()->addCondition( |
152
|
1 |
|
$aTable, |
153
|
1 |
|
$this->correctColumn($aTable, $column), |
154
|
1 |
|
IQueryBuilder::OPERATION_REXP, |
155
|
1 |
|
$pattern |
156
|
1 |
|
); |
157
|
1 |
|
return $this; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @param string $table |
162
|
|
|
* @param string $column |
163
|
|
|
* @param string $min |
164
|
|
|
* @param string $max |
165
|
|
|
* @throws MapperException |
166
|
|
|
* @return $this |
167
|
|
|
*/ |
168
|
1 |
|
public function between(string $table, string $column, $min, $max): self |
169
|
|
|
{ |
170
|
1 |
|
$aTable = $this->correctTable($table); |
171
|
1 |
|
$this->getQueryBuilder()->addCondition($aTable, $this->correctColumn($aTable, $column), IQueryBuilder::OPERATION_GTE, $min); |
172
|
1 |
|
$this->getQueryBuilder()->addCondition($aTable, $this->correctColumn($aTable, $column), IQueryBuilder::OPERATION_LTE, $max); |
173
|
1 |
|
return $this; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param string $table |
178
|
|
|
* @param string $column |
179
|
|
|
* @throws MapperException |
180
|
|
|
* @return $this |
181
|
|
|
*/ |
182
|
1 |
|
public function null(string $table, string $column): self |
183
|
|
|
{ |
184
|
1 |
|
$aTable = $this->correctTable($table); |
185
|
1 |
|
$this->getQueryBuilder()->addCondition( |
186
|
1 |
|
$aTable, |
187
|
1 |
|
$this->correctColumn($aTable, $column), |
188
|
1 |
|
IQueryBuilder::OPERATION_NULL |
189
|
1 |
|
); |
190
|
1 |
|
return $this; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @param string $table |
195
|
|
|
* @param string $column |
196
|
|
|
* @throws MapperException |
197
|
|
|
* @return $this |
198
|
|
|
*/ |
199
|
1 |
|
public function notNull(string $table, string $column): self |
200
|
|
|
{ |
201
|
1 |
|
$aTable = $this->correctTable($table); |
202
|
1 |
|
$this->getQueryBuilder()->addCondition( |
203
|
1 |
|
$aTable, |
204
|
1 |
|
$this->correctColumn($aTable, $column), |
205
|
1 |
|
IQueryBuilder::OPERATION_NNULL |
206
|
1 |
|
); |
207
|
1 |
|
return $this; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @param string $table |
212
|
|
|
* @param string $column |
213
|
|
|
* @param array<string|int|float> $values |
214
|
|
|
* @throws MapperException |
215
|
|
|
* @return $this |
216
|
|
|
*/ |
217
|
1 |
|
public function in(string $table, string $column, array $values): self |
218
|
|
|
{ |
219
|
1 |
|
$aTable = $this->correctTable($table); |
220
|
1 |
|
$this->getQueryBuilder()->addCondition( |
221
|
1 |
|
$aTable, |
222
|
1 |
|
$this->correctColumn($aTable, $column), |
223
|
1 |
|
IQueryBuilder::OPERATION_IN, |
224
|
1 |
|
$values |
225
|
1 |
|
); |
226
|
1 |
|
return $this; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @param string $table |
231
|
|
|
* @param string $column |
232
|
|
|
* @param array<string|int|float> $values |
233
|
|
|
* @throws MapperException |
234
|
|
|
* @return $this |
235
|
|
|
*/ |
236
|
1 |
|
public function notIn(string $table, string $column, array $values): self |
237
|
|
|
{ |
238
|
1 |
|
$aTable = $this->correctTable($table); |
239
|
1 |
|
$this->getQueryBuilder()->addCondition( |
240
|
1 |
|
$aTable, |
241
|
1 |
|
$this->correctColumn($aTable, $column), |
242
|
1 |
|
IQueryBuilder::OPERATION_NIN, |
243
|
1 |
|
$values |
244
|
1 |
|
); |
245
|
1 |
|
return $this; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param string|string[]|callable $operation |
250
|
|
|
* @param string $prefix |
251
|
|
|
* @param mixed $value |
252
|
|
|
* @return $this |
253
|
|
|
*/ |
254
|
|
|
public function raw($operation, string $prefix = '', $value = null): self |
255
|
|
|
{ |
256
|
|
|
$this->getQueryBuilder()->addRawCondition( |
257
|
|
|
$operation, |
258
|
|
|
$prefix, |
259
|
|
|
$value |
260
|
|
|
); |
261
|
|
|
return $this; |
262
|
|
|
} |
263
|
|
|
|
264
|
1 |
|
public function useAnd(): self |
265
|
|
|
{ |
266
|
1 |
|
$this->getQueryBuilder()->setRelations(IQueryBuilder::RELATION_AND); |
267
|
1 |
|
return $this; |
268
|
|
|
} |
269
|
|
|
|
270
|
1 |
|
public function useOr(): self |
271
|
|
|
{ |
272
|
1 |
|
$this->getQueryBuilder()->setRelations(IQueryBuilder::RELATION_OR); |
273
|
1 |
|
return $this; |
274
|
|
|
} |
275
|
|
|
|
276
|
1 |
|
public function limit(?int $limit): self |
277
|
|
|
{ |
278
|
1 |
|
$this->getQueryBuilder()->setLimit($limit); |
279
|
1 |
|
return $this; |
280
|
|
|
} |
281
|
|
|
|
282
|
1 |
|
public function offset(?int $offset): self |
283
|
|
|
{ |
284
|
1 |
|
$this->getQueryBuilder()->setOffset($offset); |
285
|
1 |
|
return $this; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Add ordering by |
290
|
|
|
* @param string $table |
291
|
|
|
* @param string $column |
292
|
|
|
* @param string $direction |
293
|
|
|
* @throws MapperException |
294
|
|
|
* @return $this |
295
|
|
|
*/ |
296
|
1 |
|
public function orderBy(string $table, string $column, string $direction = IQueryBuilder::ORDER_ASC): self |
297
|
|
|
{ |
298
|
1 |
|
$aTable = $this->correctTable($table); |
299
|
1 |
|
$this->getQueryBuilder()->addOrderBy($aTable, $this->correctColumn($aTable, $column), $direction); |
300
|
1 |
|
return $this; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Add grouping by |
305
|
|
|
* @param string $table |
306
|
|
|
* @param string $column |
307
|
|
|
* @throws MapperException |
308
|
|
|
* @return $this |
309
|
|
|
*/ |
310
|
1 |
|
public function groupBy(string $table, string $column): self |
311
|
|
|
{ |
312
|
1 |
|
$aTable = $this->correctTable($table); |
313
|
1 |
|
$this->getQueryBuilder()->addGroupBy($aTable, $this->correctColumn($aTable, $column)); |
314
|
1 |
|
return $this; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Add child which will be mounted to results |
319
|
|
|
* @param string $childAlias |
320
|
|
|
* @param string $joinType |
321
|
|
|
* @param string $parentAlias |
322
|
|
|
* @param string $customAlias |
323
|
|
|
* @throws MapperException |
324
|
|
|
* @return $this |
325
|
|
|
*/ |
326
|
9 |
|
public function child(string $childAlias, string $joinType = IQueryBuilder::JOIN_LEFT, string $parentAlias = '', string $customAlias = ''): self |
327
|
|
|
{ |
328
|
|
|
// from mapper - children's mapper then there table name |
329
|
9 |
|
if (!empty($parentAlias)) { |
330
|
3 |
|
$parentLookup = $this->recordLookup($parentAlias); |
331
|
3 |
|
if ($parentLookup && $parentLookup->getRecord()) { |
332
|
3 |
|
$parentRecord = $parentLookup->getRecord(); |
333
|
|
|
} |
334
|
|
|
} else { |
335
|
8 |
|
$parentRecord = $this->getBasicRecord(); |
336
|
8 |
|
$parentAlias = $parentRecord->getMapper()->getAlias(); |
337
|
|
|
} |
338
|
9 |
|
if (empty($parentRecord)) { |
339
|
1 |
|
throw new MapperException(sprintf('Unknown record for parent alias *%s*', $parentAlias)); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** @var array<string|int, ForeignKey> $parentKeys */ |
343
|
8 |
|
$parentKeys = $parentRecord->getMapper()->getForeignKeys(); |
344
|
8 |
|
if (!isset($parentKeys[$childAlias])) { |
345
|
1 |
|
throw new MapperException(sprintf('Unknown alias *%s* in mapper for parent *%s*', $childAlias, $parentAlias)); |
346
|
|
|
} |
347
|
|
|
|
348
|
8 |
|
$parentKey = $parentKeys[$childAlias]; |
349
|
8 |
|
$parentRelations = $parentRecord->getMapper()->getRelations(); |
|
|
|
|
350
|
8 |
|
if (empty($parentRelations[$parentKey->getLocalEntryKey()])) { |
351
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for parent *%s*', $parentKey->getLocalEntryKey(), $parentAlias)); |
352
|
|
|
} |
353
|
|
|
|
354
|
7 |
|
$childTableAlias = empty($customAlias) ? $childAlias : $customAlias; |
355
|
7 |
|
$childLookup = $this->recordLookup($childTableAlias, $childAlias); |
356
|
7 |
|
if (empty($childLookup) || empty($childLookup->getRecord())) { |
357
|
|
|
// might never happens - part already checked, so it must exists |
358
|
|
|
// @codeCoverageIgnoreStart |
359
|
|
|
throw new MapperException(sprintf('Unknown record for child alias *%s*', $childAlias)); |
360
|
|
|
} |
361
|
|
|
// @codeCoverageIgnoreEnd |
362
|
|
|
|
363
|
7 |
|
$childRecord = $childLookup->getRecord(); |
364
|
7 |
|
$childRelations = $childRecord->getMapper()->getRelations(); |
365
|
7 |
|
if (empty($childRelations[$parentKey->getRemoteEntryKey()])) { |
366
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for child *%s*', $parentKey->getRemoteEntryKey(), $childAlias)); |
367
|
|
|
} |
368
|
|
|
|
369
|
6 |
|
if ($parentRecord->getMapper()->getSource() != $childRecord->getMapper()->getSource()) { |
370
|
1 |
|
throw new MapperException(sprintf('Parent *%s* and child *%s* must both have the same source', $parentAlias, $childAlias)); |
371
|
|
|
} |
372
|
|
|
|
373
|
5 |
|
$this->getQueryBuilder()->addJoin( |
374
|
5 |
|
$childAlias, |
375
|
5 |
|
$childRecord->getMapper()->getAlias(), |
376
|
5 |
|
$childRelations[$parentKey->getRemoteEntryKey()], |
377
|
5 |
|
$parentAlias, |
378
|
5 |
|
$parentRelations[$parentKey->getLocalEntryKey()], |
379
|
5 |
|
$joinType, |
380
|
5 |
|
$childTableAlias |
381
|
5 |
|
); |
382
|
|
|
|
383
|
4 |
|
return $this; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* That child is not set for chosen parent |
388
|
|
|
* @param string $childAlias |
389
|
|
|
* @param string $table |
390
|
|
|
* @param string $column |
391
|
|
|
* @param string $parentAlias |
392
|
|
|
* @throws MapperException |
393
|
|
|
* @return $this |
394
|
|
|
*/ |
395
|
2 |
|
public function childNotExist(string $childAlias, string $table, string $column, string $parentAlias = ''): self |
396
|
|
|
{ |
397
|
2 |
|
$this->child($childAlias, IQueryBuilder::JOIN_LEFT_OUTER, $parentAlias); |
398
|
1 |
|
$aTable = $this->correctTable($table); |
399
|
1 |
|
$this->getQueryBuilder()->addCondition( |
400
|
1 |
|
$aTable, |
401
|
1 |
|
$this->correctColumn($aTable, $column), |
402
|
1 |
|
IQueryBuilder::OPERATION_NULL |
403
|
1 |
|
); |
404
|
1 |
|
return $this; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Return count of all records selected by params |
409
|
|
|
* @throws MapperException |
410
|
|
|
* @return int |
411
|
|
|
*/ |
412
|
|
|
abstract public function getCount(): int; |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* Return records |
416
|
|
|
* @throws MapperException |
417
|
|
|
* @return ARecord[] |
418
|
|
|
*/ |
419
|
|
|
abstract public function getResults(): array; |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* @param string $table |
423
|
|
|
* @throws MapperException |
424
|
|
|
* @return string |
425
|
|
|
*/ |
426
|
8 |
|
protected function correctTable(string $table): string |
427
|
|
|
{ |
428
|
8 |
|
return empty($table) ? $this->getBasicRecord()->getMapper()->getAlias() : $table ; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* @param string $table |
433
|
|
|
* @param string $column |
434
|
|
|
* @throws MapperException |
435
|
|
|
* @return string|int |
436
|
|
|
*/ |
437
|
8 |
|
protected function correctColumn(string $table, string $column) |
438
|
|
|
{ |
439
|
8 |
|
$record = !empty($table) ? $this->recordLookup($table)->getRecord() : $this->getBasicRecord() ; |
440
|
8 |
|
if (empty($record)) { |
441
|
|
|
// @codeCoverageIgnoreStart |
442
|
|
|
throw new MapperException(sprintf('Unknown relation table *%s*', $table)); |
443
|
|
|
} |
444
|
|
|
// @codeCoverageIgnoreEnd |
445
|
8 |
|
$relations = $record->getMapper()->getRelations(); |
446
|
8 |
|
if (empty($relations[$column])) { |
447
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for table *%s*', $column, $table)); |
448
|
|
|
} |
449
|
7 |
|
return $relations[$column]; |
450
|
|
|
} |
451
|
|
|
|
452
|
28 |
|
protected function getQueryBuilder(): Storage\Shared\QueryBuilder |
453
|
|
|
{ |
454
|
28 |
|
return $this->queryBuilder; |
455
|
|
|
} |
456
|
|
|
|
457
|
13 |
|
protected function getBasicRecord(): ARecord |
458
|
|
|
{ |
459
|
13 |
|
return $this->basicRecord; |
460
|
|
|
} |
461
|
|
|
} |
462
|
|
|
|