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
|
|
|
$value |
40
|
|
|
); |
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
|
7 |
|
IQueryBuilder::OPERATION_EQ, |
58
|
|
|
$value |
59
|
|
|
); |
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
|
|
|
$value |
79
|
|
|
); |
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
|
|
|
$value |
99
|
|
|
); |
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
|
6 |
|
IQueryBuilder::OPERATION_LIKE, |
117
|
|
|
$value |
118
|
|
|
); |
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
|
|
|
$value |
137
|
|
|
); |
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
|
|
|
$pattern |
156
|
|
|
); |
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
|
|
|
); |
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
|
|
|
); |
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
|
|
|
$values |
225
|
|
|
); |
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
|
|
|
$values |
244
|
|
|
); |
245
|
1 |
|
return $this; |
246
|
|
|
} |
247
|
|
|
|
248
|
1 |
|
public function useAnd(): self |
249
|
|
|
{ |
250
|
1 |
|
$this->getQueryBuilder()->setRelations(IQueryBuilder::RELATION_AND); |
251
|
1 |
|
return $this; |
252
|
|
|
} |
253
|
|
|
|
254
|
1 |
|
public function useOr(): self |
255
|
|
|
{ |
256
|
1 |
|
$this->getQueryBuilder()->setRelations(IQueryBuilder::RELATION_OR); |
257
|
1 |
|
return $this; |
258
|
|
|
} |
259
|
|
|
|
260
|
1 |
|
public function limit(?int $limit): self |
261
|
|
|
{ |
262
|
1 |
|
$this->getQueryBuilder()->setLimit($limit); |
263
|
1 |
|
return $this; |
264
|
|
|
} |
265
|
|
|
|
266
|
1 |
|
public function offset(?int $offset): self |
267
|
|
|
{ |
268
|
1 |
|
$this->getQueryBuilder()->setOffset($offset); |
269
|
1 |
|
return $this; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Add ordering by |
274
|
|
|
* @param string $table |
275
|
|
|
* @param string $column |
276
|
|
|
* @param string $direction |
277
|
|
|
* @throws MapperException |
278
|
|
|
* @return $this |
279
|
|
|
*/ |
280
|
1 |
|
public function orderBy(string $table, string $column, string $direction = IQueryBuilder::ORDER_ASC): self |
281
|
|
|
{ |
282
|
1 |
|
$aTable = $this->correctTable($table); |
283
|
1 |
|
$this->getQueryBuilder()->addOrderBy($aTable, $this->correctColumn($aTable, $column), $direction); |
284
|
1 |
|
return $this; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Add grouping by |
289
|
|
|
* @param string $table |
290
|
|
|
* @param string $column |
291
|
|
|
* @throws MapperException |
292
|
|
|
* @return $this |
293
|
|
|
*/ |
294
|
1 |
|
public function groupBy(string $table, string $column): self |
295
|
|
|
{ |
296
|
1 |
|
$aTable = $this->correctTable($table); |
297
|
1 |
|
$this->getQueryBuilder()->addGroupBy($aTable, $this->correctColumn($aTable, $column)); |
298
|
1 |
|
return $this; |
299
|
|
|
} |
300
|
|
|
|
301
|
|
|
/** |
302
|
|
|
* Add child which will be mounted to results |
303
|
|
|
* @param string $childAlias |
304
|
|
|
* @param string $joinType |
305
|
|
|
* @param string $parentAlias |
306
|
|
|
* @param string $customAlias |
307
|
|
|
* @throws MapperException |
308
|
|
|
* @return $this |
309
|
|
|
*/ |
310
|
9 |
|
public function child(string $childAlias, string $joinType = IQueryBuilder::JOIN_LEFT, string $parentAlias = '', string $customAlias = ''): self |
311
|
|
|
{ |
312
|
|
|
// from mapper - children's mapper then there table name |
313
|
9 |
|
if (!empty($parentAlias)) { |
314
|
3 |
|
$parentLookup = $this->recordLookup($parentAlias); |
315
|
3 |
|
if ($parentLookup && $parentLookup->getRecord()) { |
316
|
3 |
|
$parentRecord = $parentLookup->getRecord(); |
317
|
|
|
} |
318
|
|
|
} else { |
319
|
8 |
|
$parentRecord = $this->getBasicRecord(); |
320
|
8 |
|
$parentAlias = $parentRecord->getMapper()->getAlias(); |
321
|
|
|
} |
322
|
9 |
|
if (empty($parentRecord)) { |
323
|
1 |
|
throw new MapperException(sprintf('Unknown record for parent alias *%s*', $parentAlias)); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** @var array<string|int, ForeignKey> $parentKeys */ |
327
|
8 |
|
$parentKeys = $parentRecord->getMapper()->getForeignKeys(); |
328
|
8 |
|
if (!isset($parentKeys[$childAlias])) { |
329
|
1 |
|
throw new MapperException(sprintf('Unknown alias *%s* in mapper for parent *%s*', $childAlias, $parentAlias)); |
330
|
|
|
} |
331
|
|
|
|
332
|
8 |
|
$parentKey = $parentKeys[$childAlias]; |
333
|
8 |
|
$parentRelations = $parentRecord->getMapper()->getRelations(); |
|
|
|
|
334
|
8 |
|
if (empty($parentRelations[$parentKey->getLocalEntryKey()])) { |
335
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for parent *%s*', $parentKey->getLocalEntryKey(), $parentAlias)); |
336
|
|
|
} |
337
|
|
|
|
338
|
7 |
|
$childTableAlias = empty($customAlias) ? $childAlias : $customAlias; |
339
|
7 |
|
$childLookup = $this->recordLookup($childTableAlias, $childAlias); |
340
|
7 |
|
if (empty($childLookup) || empty($childLookup->getRecord())) { |
341
|
|
|
// might never happens - part already checked, so it must exists |
342
|
|
|
// @codeCoverageIgnoreStart |
343
|
|
|
throw new MapperException(sprintf('Unknown record for child alias *%s*', $childAlias)); |
344
|
|
|
} |
345
|
|
|
// @codeCoverageIgnoreEnd |
346
|
|
|
|
347
|
7 |
|
$childRecord = $childLookup->getRecord(); |
348
|
7 |
|
$childRelations = $childRecord->getMapper()->getRelations(); |
349
|
7 |
|
if (empty($childRelations[$parentKey->getRemoteEntryKey()])) { |
350
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for child *%s*', $parentKey->getRemoteEntryKey(), $childAlias)); |
351
|
|
|
} |
352
|
|
|
|
353
|
6 |
|
if ($parentRecord->getMapper()->getSource() != $childRecord->getMapper()->getSource()) { |
354
|
1 |
|
throw new MapperException(sprintf('Parent *%s* and child *%s* must both have the same source', $parentAlias, $childAlias)); |
355
|
|
|
} |
356
|
|
|
|
357
|
5 |
|
$this->getQueryBuilder()->addJoin( |
358
|
5 |
|
$childAlias, |
359
|
5 |
|
$childRecord->getMapper()->getAlias(), |
360
|
5 |
|
$childRelations[$parentKey->getRemoteEntryKey()], |
361
|
|
|
$parentAlias, |
362
|
5 |
|
$parentRelations[$parentKey->getLocalEntryKey()], |
363
|
|
|
$joinType, |
364
|
|
|
$childTableAlias |
365
|
|
|
); |
366
|
|
|
|
367
|
4 |
|
return $this; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* That child is not set for chosen parent |
372
|
|
|
* @param string $childAlias |
373
|
|
|
* @param string $table |
374
|
|
|
* @param string $column |
375
|
|
|
* @param string $parentAlias |
376
|
|
|
* @throws MapperException |
377
|
|
|
* @return $this |
378
|
|
|
*/ |
379
|
2 |
|
public function childNotExist(string $childAlias, string $table, string $column, string $parentAlias = ''): self |
380
|
|
|
{ |
381
|
2 |
|
$this->child($childAlias, IQueryBuilder::JOIN_LEFT_OUTER, $parentAlias); |
382
|
1 |
|
$aTable = $this->correctTable($table); |
383
|
1 |
|
$this->getQueryBuilder()->addCondition( |
384
|
1 |
|
$aTable, |
385
|
1 |
|
$this->correctColumn($aTable, $column), |
386
|
1 |
|
IQueryBuilder::OPERATION_NULL |
387
|
|
|
); |
388
|
1 |
|
return $this; |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Return count of all records selected by params |
393
|
|
|
* @throws MapperException |
394
|
|
|
* @return int |
395
|
|
|
*/ |
396
|
|
|
abstract public function getCount(): int; |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* Return records |
400
|
|
|
* @throws MapperException |
401
|
|
|
* @return ARecord[] |
402
|
|
|
*/ |
403
|
|
|
abstract public function getResults(): array; |
404
|
|
|
|
405
|
|
|
/** |
406
|
|
|
* @param string $table |
407
|
|
|
* @throws MapperException |
408
|
|
|
* @return string |
409
|
|
|
*/ |
410
|
8 |
|
protected function correctTable(string $table): string |
411
|
|
|
{ |
412
|
8 |
|
return empty($table) ? $this->getBasicRecord()->getMapper()->getAlias() : $table ; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* @param string $table |
417
|
|
|
* @param string $column |
418
|
|
|
* @throws MapperException |
419
|
|
|
* @return string|int |
420
|
|
|
*/ |
421
|
8 |
|
protected function correctColumn(string $table, string $column) |
422
|
|
|
{ |
423
|
8 |
|
$record = !empty($table) ? $this->recordLookup($table)->getRecord() : $this->getBasicRecord() ; |
424
|
8 |
|
if (empty($record)) { |
425
|
|
|
// @codeCoverageIgnoreStart |
426
|
|
|
throw new MapperException(sprintf('Unknown relation table *%s*', $table)); |
427
|
|
|
} |
428
|
|
|
// @codeCoverageIgnoreEnd |
429
|
8 |
|
$relations = $record->getMapper()->getRelations(); |
430
|
8 |
|
if (empty($relations[$column])) { |
431
|
1 |
|
throw new MapperException(sprintf('Unknown relation key *%s* in mapper for table *%s*', $column, $table)); |
432
|
|
|
} |
433
|
7 |
|
return $relations[$column]; |
434
|
|
|
} |
435
|
|
|
|
436
|
28 |
|
private function getQueryBuilder(): Storage\Shared\QueryBuilder |
437
|
|
|
{ |
438
|
28 |
|
return $this->queryBuilder; |
439
|
|
|
} |
440
|
|
|
|
441
|
13 |
|
private function getBasicRecord(): ARecord |
442
|
|
|
{ |
443
|
13 |
|
return $this->basicRecord; |
444
|
|
|
} |
445
|
|
|
} |
446
|
|
|
|