Completed
Push — master ( 5c161a...b70884 )
by Fabien
52:39
created
Classes/Persistence/OrderObjectFactory.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -18,42 +18,42 @@
 block discarded – undo
18 18
  */
19 19
 class OrderObjectFactory implements SingletonInterface
20 20
 {
21
-    /**
22
-     * Gets a singleton instance of this class.
23
-     *
24
-     * @return \Fab\Vidi\Persistence\OrderObjectFactory|object
25
-     */
26
-    public static function getInstance()
27
-    {
28
-        return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\OrderObjectFactory::class);
29
-    }
30
-
31
-    /**
32
-     * Returns an order object.
33
-     *
34
-     * @param string $dataType
35
-     * @return Order|object
36
-     */
37
-    public function getOrder($dataType = '')
38
-    {
39
-        // Default ordering
40
-        $order = Tca::table($dataType)->getDefaultOrderings();
41
-
42
-        // Retrieve a possible id of the column from the request
43
-        $orderings = GeneralUtility::_GP('order');
44
-
45
-        if (is_array($orderings) && isset($orderings[0])) {
46
-            $columnPosition = $orderings[0]['column'];
47
-            $direction = $orderings[0]['dir'];
48
-
49
-            if ($columnPosition > 0) {
50
-                $field = Tca::grid()->getFieldNameByPosition($columnPosition);
51
-
52
-                $order = array(
53
-                    $field => strtoupper($direction)
54
-                );
55
-            }
56
-        }
57
-        return GeneralUtility::makeInstance(Order::class, $order);
58
-    }
21
+	/**
22
+	 * Gets a singleton instance of this class.
23
+	 *
24
+	 * @return \Fab\Vidi\Persistence\OrderObjectFactory|object
25
+	 */
26
+	public static function getInstance()
27
+	{
28
+		return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\OrderObjectFactory::class);
29
+	}
30
+
31
+	/**
32
+	 * Returns an order object.
33
+	 *
34
+	 * @param string $dataType
35
+	 * @return Order|object
36
+	 */
37
+	public function getOrder($dataType = '')
38
+	{
39
+		// Default ordering
40
+		$order = Tca::table($dataType)->getDefaultOrderings();
41
+
42
+		// Retrieve a possible id of the column from the request
43
+		$orderings = GeneralUtility::_GP('order');
44
+
45
+		if (is_array($orderings) && isset($orderings[0])) {
46
+			$columnPosition = $orderings[0]['column'];
47
+			$direction = $orderings[0]['dir'];
48
+
49
+			if ($columnPosition > 0) {
50
+				$field = Tca::grid()->getFieldNameByPosition($columnPosition);
51
+
52
+				$order = array(
53
+					$field => strtoupper($direction)
54
+				);
55
+			}
56
+		}
57
+		return GeneralUtility::makeInstance(Order::class, $order);
58
+	}
59 59
 }
Please login to merge, or discard this patch.
Classes/Persistence/Matcher.php 1 patch
Indentation   +495 added lines, -495 removed lines patch added patch discarded remove patch
@@ -14,499 +14,499 @@
 block discarded – undo
14 14
  */
15 15
 class Matcher
16 16
 {
17
-    /**
18
-     * The logical OR
19
-     */
20
-    public const LOGICAL_OR = 'logicalOr';
21
-
22
-    /**
23
-     * The logical AND
24
-     */
25
-    public const LOGICAL_AND = 'logicalAnd';
26
-
27
-    /**
28
-     * @var string
29
-     */
30
-    protected $dataType = '';
31
-
32
-    /**
33
-     * @var string
34
-     */
35
-    protected $searchTerm = '';
36
-
37
-    /**
38
-     * @var array
39
-     */
40
-    protected $supportedOperators = [
41
-        '=' => 'equals',
42
-        '!=' => 'notEquals',
43
-        'in' => 'in',
44
-        'like' => 'like',
45
-        '>' => 'greaterThan',
46
-        '>=' => 'greaterThanOrEqual',
47
-        '<' => 'lessThan',
48
-        '<=' => 'lessThanOrEqual',
49
-    ];
50
-
51
-    /**
52
-     * @var array
53
-     */
54
-    protected $equals = [];
55
-
56
-    /**
57
-     * @var array
58
-     */
59
-    protected $notEquals = [];
60
-
61
-    /**
62
-     * @var array
63
-     */
64
-    protected $greaterThan = [];
65
-
66
-    /**
67
-     * @var array
68
-     */
69
-    protected $greaterThanOrEqual = [];
70
-
71
-    /**
72
-     * @var array
73
-     */
74
-    protected $lessThan = [];
75
-
76
-    /**
77
-     * @var array
78
-     */
79
-    protected $lessThanOrEqual = [];
80
-
81
-    /**
82
-     * @var array
83
-     */
84
-    protected $in = [];
85
-
86
-    /**
87
-     * @var array
88
-     */
89
-    protected $like = [];
90
-
91
-    /**
92
-     * @var array
93
-     */
94
-    protected $matches = [];
95
-
96
-    /**
97
-     * @var string
98
-     */
99
-    protected $defaultLogicalSeparator = self::LOGICAL_AND;
100
-
101
-    /**
102
-     * @var string
103
-     */
104
-    protected $logicalSeparatorForEquals = self::LOGICAL_AND;
105
-
106
-    /**
107
-     * @var string
108
-     */
109
-    protected $logicalSeparatorForGreaterThan = self::LOGICAL_AND;
110
-
111
-    /**
112
-     * @var string
113
-     */
114
-    protected $logicalSeparatorForGreaterThanOrEqual = self::LOGICAL_AND;
115
-
116
-    /**
117
-     * @var string
118
-     */
119
-    protected $logicalSeparatorForLessThan = self::LOGICAL_AND;
120
-
121
-    /**
122
-     * @var string
123
-     */
124
-    protected $logicalSeparatorForLessThanOrEqual = self::LOGICAL_AND;
125
-
126
-    /**
127
-     * @var string
128
-     */
129
-    protected $logicalSeparatorForIn = self::LOGICAL_AND;
130
-
131
-    /**
132
-     * @var string
133
-     */
134
-    protected $logicalSeparatorForLike = self::LOGICAL_AND;
135
-
136
-    /**
137
-     * @var string
138
-     */
139
-    protected $logicalSeparatorForSearchTerm = self::LOGICAL_OR;
140
-
141
-    /**
142
-     * Constructs a new Matcher
143
-     *
144
-     * @param array $matches associative [$field => $value]
145
-     * @param string $dataType which corresponds to an entry of the TCA (table name).
146
-     */
147
-    public function __construct($matches = [], $dataType = '')
148
-    {
149
-        $this->dataType = $dataType;
150
-        $this->matches = $matches;
151
-    }
152
-
153
-    /**
154
-     * @param string $searchTerm
155
-     * @return \Fab\Vidi\Persistence\Matcher
156
-     */
157
-    public function setSearchTerm($searchTerm): Matcher
158
-    {
159
-        $this->searchTerm = $searchTerm;
160
-        return $this;
161
-    }
162
-
163
-    /**
164
-     * @return string
165
-     */
166
-    public function getSearchTerm(): string
167
-    {
168
-        return $this->searchTerm;
169
-    }
170
-
171
-    /**
172
-     * @return array
173
-     */
174
-    public function getEquals(): array
175
-    {
176
-        return $this->equals;
177
-    }
178
-
179
-    /**
180
-     * @param $fieldNameAndPath
181
-     * @param $operand
182
-     * @return $this
183
-     */
184
-    public function equals($fieldNameAndPath, $operand): self
185
-    {
186
-        $this->equals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
187
-        return $this;
188
-    }
189
-
190
-    /**
191
-     * @return array
192
-     */
193
-    public function getNotEquals(): array
194
-    {
195
-        return $this->notEquals;
196
-    }
197
-
198
-    /**
199
-     * @param $fieldNameAndPath
200
-     * @param $operand
201
-     * @return $this
202
-     */
203
-    public function notEquals($fieldNameAndPath, $operand): self
204
-    {
205
-        $this->notEquals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
206
-        return $this;
207
-    }
208
-
209
-    /**
210
-     * @return array
211
-     */
212
-    public function getGreaterThan(): array
213
-    {
214
-        return $this->greaterThan;
215
-    }
216
-
217
-    /**
218
-     * @param $fieldNameAndPath
219
-     * @param $operand
220
-     * @return $this
221
-     */
222
-    public function greaterThan($fieldNameAndPath, $operand): self
223
-    {
224
-        $this->greaterThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
225
-        return $this;
226
-    }
227
-
228
-    /**
229
-     * @return array
230
-     */
231
-    public function getGreaterThanOrEqual(): array
232
-    {
233
-        return $this->greaterThanOrEqual;
234
-    }
235
-
236
-    /**
237
-     * @param $fieldNameAndPath
238
-     * @param $operand
239
-     * @return $this
240
-     */
241
-    public function greaterThanOrEqual($fieldNameAndPath, $operand): self
242
-    {
243
-        $this->greaterThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
244
-        return $this;
245
-    }
246
-
247
-    /**
248
-     * @return array
249
-     */
250
-    public function getLessThan(): array
251
-    {
252
-        return $this->lessThan;
253
-    }
254
-
255
-    /**
256
-     * @param $fieldNameAndPath
257
-     * @param $operand
258
-     * @return $this
259
-     */
260
-    public function lessThan($fieldNameAndPath, $operand): self
261
-    {
262
-        $this->lessThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
263
-        return $this;
264
-    }
265
-
266
-    /**
267
-     * @return array
268
-     */
269
-    public function getLessThanOrEqual(): array
270
-    {
271
-        return $this->lessThanOrEqual;
272
-    }
273
-
274
-    /**
275
-     * @param $fieldNameAndPath
276
-     * @param $operand
277
-     * @return $this
278
-     */
279
-    public function lessThanOrEqual($fieldNameAndPath, $operand): self
280
-    {
281
-        $this->lessThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
282
-        return $this;
283
-    }
284
-
285
-    /**
286
-     * @return array
287
-     */
288
-    public function getLike(): array
289
-    {
290
-        return $this->like;
291
-    }
292
-
293
-    /**
294
-     * @param $fieldNameAndPath
295
-     * @param $operand
296
-     * @return $this
297
-     */
298
-    public function in($fieldNameAndPath, $operand): self
299
-    {
300
-        $this->in[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
301
-        return $this;
302
-    }
303
-
304
-    /**
305
-     * @return array
306
-     */
307
-    public function getIn(): array
308
-    {
309
-        return $this->in;
310
-    }
311
-
312
-    /**
313
-     * @param $fieldNameAndPath
314
-     * @param $operand
315
-     * @param bool $addWildCard
316
-     * @return $this
317
-     */
318
-    public function like($fieldNameAndPath, $operand, $addWildCard = true): self
319
-    {
320
-        $wildCardSymbol = $addWildCard ? '%' : '';
321
-        $this->like[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $wildCardSymbol . $operand . $wildCardSymbol];
322
-        return $this;
323
-    }
324
-
325
-    /**
326
-     * @return string
327
-     */
328
-    public function getDefaultLogicalSeparator(): string
329
-    {
330
-        return $this->defaultLogicalSeparator;
331
-    }
332
-
333
-    /**
334
-     * @param string $defaultLogicalSeparator
335
-     * @return $this
336
-     */
337
-    public function setDefaultLogicalSeparator($defaultLogicalSeparator): self
338
-    {
339
-        $this->defaultLogicalSeparator = $defaultLogicalSeparator;
340
-        return $this;
341
-    }
342
-
343
-    /**
344
-     * @return string
345
-     */
346
-    public function getLogicalSeparatorForEquals(): string
347
-    {
348
-        return $this->logicalSeparatorForEquals;
349
-    }
350
-
351
-    /**
352
-     * @param string $logicalSeparatorForEquals
353
-     * @return $this
354
-     */
355
-    public function setLogicalSeparatorForEquals($logicalSeparatorForEquals): self
356
-    {
357
-        $this->logicalSeparatorForEquals = $logicalSeparatorForEquals;
358
-        return $this;
359
-    }
360
-
361
-    /**
362
-     * @return string
363
-     */
364
-    public function getLogicalSeparatorForGreaterThan(): string
365
-    {
366
-        return $this->logicalSeparatorForGreaterThan;
367
-    }
368
-
369
-    /**
370
-     * @param string $logicalSeparatorForGreaterThan
371
-     * @return $this
372
-     */
373
-    public function setLogicalSeparatorForGreaterThan($logicalSeparatorForGreaterThan): self
374
-    {
375
-        $this->logicalSeparatorForGreaterThan = $logicalSeparatorForGreaterThan;
376
-        return $this;
377
-    }
378
-
379
-    /**
380
-     * @return string
381
-     */
382
-    public function getLogicalSeparatorForGreaterThanOrEqual(): string
383
-    {
384
-        return $this->logicalSeparatorForGreaterThanOrEqual;
385
-    }
386
-
387
-    /**
388
-     * @param string $logicalSeparatorForGreaterThanOrEqual
389
-     * @return $this
390
-     */
391
-    public function setLogicalSeparatorForGreaterThanOrEqual($logicalSeparatorForGreaterThanOrEqual): self
392
-    {
393
-        $this->logicalSeparatorForGreaterThanOrEqual = $logicalSeparatorForGreaterThanOrEqual;
394
-        return $this;
395
-    }
396
-
397
-    /**
398
-     * @return string
399
-     */
400
-    public function getLogicalSeparatorForLessThan(): string
401
-    {
402
-        return $this->logicalSeparatorForLessThan;
403
-    }
404
-
405
-    /**
406
-     * @param string $logicalSeparatorForLessThan
407
-     * @return $this
408
-     */
409
-    public function setLogicalSeparatorForLessThan($logicalSeparatorForLessThan): self
410
-    {
411
-        $this->logicalSeparatorForLessThan = $logicalSeparatorForLessThan;
412
-        return $this;
413
-    }
414
-
415
-    /**
416
-     * @return string
417
-     */
418
-    public function getLogicalSeparatorForLessThanOrEqual(): string
419
-    {
420
-        return $this->logicalSeparatorForLessThanOrEqual;
421
-    }
422
-
423
-    /**
424
-     * @param string $logicalSeparatorForLessThanOrEqual
425
-     * @return $this
426
-     */
427
-    public function setLogicalSeparatorForLessThanOrEqual($logicalSeparatorForLessThanOrEqual): self
428
-    {
429
-        $this->logicalSeparatorForLessThanOrEqual = $logicalSeparatorForLessThanOrEqual;
430
-        return $this;
431
-    }
432
-
433
-    /**
434
-     * @return string
435
-     */
436
-    public function getLogicalSeparatorForIn(): string
437
-    {
438
-        return $this->logicalSeparatorForIn;
439
-    }
440
-
441
-    /**
442
-     * @param string $logicalSeparatorForIn
443
-     * @return $this
444
-     */
445
-    public function setLogicalSeparatorForIn($logicalSeparatorForIn): self
446
-    {
447
-        $this->logicalSeparatorForIn = $logicalSeparatorForIn;
448
-        return $this;
449
-    }
450
-
451
-    /**
452
-     * @return string
453
-     */
454
-    public function getLogicalSeparatorForLike(): string
455
-    {
456
-        return $this->logicalSeparatorForLike;
457
-    }
458
-
459
-    /**
460
-     * @param string $logicalSeparatorForLike
461
-     * @return $this
462
-     */
463
-    public function setLogicalSeparatorForLike($logicalSeparatorForLike): self
464
-    {
465
-        $this->logicalSeparatorForLike = $logicalSeparatorForLike;
466
-        return $this;
467
-    }
468
-
469
-    /**
470
-     * @return string
471
-     */
472
-    public function getLogicalSeparatorForSearchTerm(): string
473
-    {
474
-        return $this->logicalSeparatorForSearchTerm;
475
-    }
476
-
477
-    /**
478
-     * @param string $logicalSeparatorForSearchTerm
479
-     * @return $this
480
-     */
481
-    public function setLogicalSeparatorForSearchTerm($logicalSeparatorForSearchTerm): self
482
-    {
483
-        $this->logicalSeparatorForSearchTerm = $logicalSeparatorForSearchTerm;
484
-        return $this;
485
-    }
486
-
487
-    /**
488
-     * @return array
489
-     */
490
-    public function getSupportedOperators(): array
491
-    {
492
-        return $this->supportedOperators;
493
-    }
494
-
495
-    /**
496
-     * @return string
497
-     */
498
-    public function getDataType(): string
499
-    {
500
-        return $this->dataType;
501
-    }
502
-
503
-    /**
504
-     * @param string $dataType
505
-     * @return $this
506
-     */
507
-    public function setDataType($dataType): self
508
-    {
509
-        $this->dataType = $dataType;
510
-        return $this;
511
-    }
17
+	/**
18
+	 * The logical OR
19
+	 */
20
+	public const LOGICAL_OR = 'logicalOr';
21
+
22
+	/**
23
+	 * The logical AND
24
+	 */
25
+	public const LOGICAL_AND = 'logicalAnd';
26
+
27
+	/**
28
+	 * @var string
29
+	 */
30
+	protected $dataType = '';
31
+
32
+	/**
33
+	 * @var string
34
+	 */
35
+	protected $searchTerm = '';
36
+
37
+	/**
38
+	 * @var array
39
+	 */
40
+	protected $supportedOperators = [
41
+		'=' => 'equals',
42
+		'!=' => 'notEquals',
43
+		'in' => 'in',
44
+		'like' => 'like',
45
+		'>' => 'greaterThan',
46
+		'>=' => 'greaterThanOrEqual',
47
+		'<' => 'lessThan',
48
+		'<=' => 'lessThanOrEqual',
49
+	];
50
+
51
+	/**
52
+	 * @var array
53
+	 */
54
+	protected $equals = [];
55
+
56
+	/**
57
+	 * @var array
58
+	 */
59
+	protected $notEquals = [];
60
+
61
+	/**
62
+	 * @var array
63
+	 */
64
+	protected $greaterThan = [];
65
+
66
+	/**
67
+	 * @var array
68
+	 */
69
+	protected $greaterThanOrEqual = [];
70
+
71
+	/**
72
+	 * @var array
73
+	 */
74
+	protected $lessThan = [];
75
+
76
+	/**
77
+	 * @var array
78
+	 */
79
+	protected $lessThanOrEqual = [];
80
+
81
+	/**
82
+	 * @var array
83
+	 */
84
+	protected $in = [];
85
+
86
+	/**
87
+	 * @var array
88
+	 */
89
+	protected $like = [];
90
+
91
+	/**
92
+	 * @var array
93
+	 */
94
+	protected $matches = [];
95
+
96
+	/**
97
+	 * @var string
98
+	 */
99
+	protected $defaultLogicalSeparator = self::LOGICAL_AND;
100
+
101
+	/**
102
+	 * @var string
103
+	 */
104
+	protected $logicalSeparatorForEquals = self::LOGICAL_AND;
105
+
106
+	/**
107
+	 * @var string
108
+	 */
109
+	protected $logicalSeparatorForGreaterThan = self::LOGICAL_AND;
110
+
111
+	/**
112
+	 * @var string
113
+	 */
114
+	protected $logicalSeparatorForGreaterThanOrEqual = self::LOGICAL_AND;
115
+
116
+	/**
117
+	 * @var string
118
+	 */
119
+	protected $logicalSeparatorForLessThan = self::LOGICAL_AND;
120
+
121
+	/**
122
+	 * @var string
123
+	 */
124
+	protected $logicalSeparatorForLessThanOrEqual = self::LOGICAL_AND;
125
+
126
+	/**
127
+	 * @var string
128
+	 */
129
+	protected $logicalSeparatorForIn = self::LOGICAL_AND;
130
+
131
+	/**
132
+	 * @var string
133
+	 */
134
+	protected $logicalSeparatorForLike = self::LOGICAL_AND;
135
+
136
+	/**
137
+	 * @var string
138
+	 */
139
+	protected $logicalSeparatorForSearchTerm = self::LOGICAL_OR;
140
+
141
+	/**
142
+	 * Constructs a new Matcher
143
+	 *
144
+	 * @param array $matches associative [$field => $value]
145
+	 * @param string $dataType which corresponds to an entry of the TCA (table name).
146
+	 */
147
+	public function __construct($matches = [], $dataType = '')
148
+	{
149
+		$this->dataType = $dataType;
150
+		$this->matches = $matches;
151
+	}
152
+
153
+	/**
154
+	 * @param string $searchTerm
155
+	 * @return \Fab\Vidi\Persistence\Matcher
156
+	 */
157
+	public function setSearchTerm($searchTerm): Matcher
158
+	{
159
+		$this->searchTerm = $searchTerm;
160
+		return $this;
161
+	}
162
+
163
+	/**
164
+	 * @return string
165
+	 */
166
+	public function getSearchTerm(): string
167
+	{
168
+		return $this->searchTerm;
169
+	}
170
+
171
+	/**
172
+	 * @return array
173
+	 */
174
+	public function getEquals(): array
175
+	{
176
+		return $this->equals;
177
+	}
178
+
179
+	/**
180
+	 * @param $fieldNameAndPath
181
+	 * @param $operand
182
+	 * @return $this
183
+	 */
184
+	public function equals($fieldNameAndPath, $operand): self
185
+	{
186
+		$this->equals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
187
+		return $this;
188
+	}
189
+
190
+	/**
191
+	 * @return array
192
+	 */
193
+	public function getNotEquals(): array
194
+	{
195
+		return $this->notEquals;
196
+	}
197
+
198
+	/**
199
+	 * @param $fieldNameAndPath
200
+	 * @param $operand
201
+	 * @return $this
202
+	 */
203
+	public function notEquals($fieldNameAndPath, $operand): self
204
+	{
205
+		$this->notEquals[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
206
+		return $this;
207
+	}
208
+
209
+	/**
210
+	 * @return array
211
+	 */
212
+	public function getGreaterThan(): array
213
+	{
214
+		return $this->greaterThan;
215
+	}
216
+
217
+	/**
218
+	 * @param $fieldNameAndPath
219
+	 * @param $operand
220
+	 * @return $this
221
+	 */
222
+	public function greaterThan($fieldNameAndPath, $operand): self
223
+	{
224
+		$this->greaterThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
225
+		return $this;
226
+	}
227
+
228
+	/**
229
+	 * @return array
230
+	 */
231
+	public function getGreaterThanOrEqual(): array
232
+	{
233
+		return $this->greaterThanOrEqual;
234
+	}
235
+
236
+	/**
237
+	 * @param $fieldNameAndPath
238
+	 * @param $operand
239
+	 * @return $this
240
+	 */
241
+	public function greaterThanOrEqual($fieldNameAndPath, $operand): self
242
+	{
243
+		$this->greaterThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
244
+		return $this;
245
+	}
246
+
247
+	/**
248
+	 * @return array
249
+	 */
250
+	public function getLessThan(): array
251
+	{
252
+		return $this->lessThan;
253
+	}
254
+
255
+	/**
256
+	 * @param $fieldNameAndPath
257
+	 * @param $operand
258
+	 * @return $this
259
+	 */
260
+	public function lessThan($fieldNameAndPath, $operand): self
261
+	{
262
+		$this->lessThan[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
263
+		return $this;
264
+	}
265
+
266
+	/**
267
+	 * @return array
268
+	 */
269
+	public function getLessThanOrEqual(): array
270
+	{
271
+		return $this->lessThanOrEqual;
272
+	}
273
+
274
+	/**
275
+	 * @param $fieldNameAndPath
276
+	 * @param $operand
277
+	 * @return $this
278
+	 */
279
+	public function lessThanOrEqual($fieldNameAndPath, $operand): self
280
+	{
281
+		$this->lessThanOrEqual[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
282
+		return $this;
283
+	}
284
+
285
+	/**
286
+	 * @return array
287
+	 */
288
+	public function getLike(): array
289
+	{
290
+		return $this->like;
291
+	}
292
+
293
+	/**
294
+	 * @param $fieldNameAndPath
295
+	 * @param $operand
296
+	 * @return $this
297
+	 */
298
+	public function in($fieldNameAndPath, $operand): self
299
+	{
300
+		$this->in[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $operand];
301
+		return $this;
302
+	}
303
+
304
+	/**
305
+	 * @return array
306
+	 */
307
+	public function getIn(): array
308
+	{
309
+		return $this->in;
310
+	}
311
+
312
+	/**
313
+	 * @param $fieldNameAndPath
314
+	 * @param $operand
315
+	 * @param bool $addWildCard
316
+	 * @return $this
317
+	 */
318
+	public function like($fieldNameAndPath, $operand, $addWildCard = true): self
319
+	{
320
+		$wildCardSymbol = $addWildCard ? '%' : '';
321
+		$this->like[] = ['fieldNameAndPath' => $fieldNameAndPath, 'operand' => $wildCardSymbol . $operand . $wildCardSymbol];
322
+		return $this;
323
+	}
324
+
325
+	/**
326
+	 * @return string
327
+	 */
328
+	public function getDefaultLogicalSeparator(): string
329
+	{
330
+		return $this->defaultLogicalSeparator;
331
+	}
332
+
333
+	/**
334
+	 * @param string $defaultLogicalSeparator
335
+	 * @return $this
336
+	 */
337
+	public function setDefaultLogicalSeparator($defaultLogicalSeparator): self
338
+	{
339
+		$this->defaultLogicalSeparator = $defaultLogicalSeparator;
340
+		return $this;
341
+	}
342
+
343
+	/**
344
+	 * @return string
345
+	 */
346
+	public function getLogicalSeparatorForEquals(): string
347
+	{
348
+		return $this->logicalSeparatorForEquals;
349
+	}
350
+
351
+	/**
352
+	 * @param string $logicalSeparatorForEquals
353
+	 * @return $this
354
+	 */
355
+	public function setLogicalSeparatorForEquals($logicalSeparatorForEquals): self
356
+	{
357
+		$this->logicalSeparatorForEquals = $logicalSeparatorForEquals;
358
+		return $this;
359
+	}
360
+
361
+	/**
362
+	 * @return string
363
+	 */
364
+	public function getLogicalSeparatorForGreaterThan(): string
365
+	{
366
+		return $this->logicalSeparatorForGreaterThan;
367
+	}
368
+
369
+	/**
370
+	 * @param string $logicalSeparatorForGreaterThan
371
+	 * @return $this
372
+	 */
373
+	public function setLogicalSeparatorForGreaterThan($logicalSeparatorForGreaterThan): self
374
+	{
375
+		$this->logicalSeparatorForGreaterThan = $logicalSeparatorForGreaterThan;
376
+		return $this;
377
+	}
378
+
379
+	/**
380
+	 * @return string
381
+	 */
382
+	public function getLogicalSeparatorForGreaterThanOrEqual(): string
383
+	{
384
+		return $this->logicalSeparatorForGreaterThanOrEqual;
385
+	}
386
+
387
+	/**
388
+	 * @param string $logicalSeparatorForGreaterThanOrEqual
389
+	 * @return $this
390
+	 */
391
+	public function setLogicalSeparatorForGreaterThanOrEqual($logicalSeparatorForGreaterThanOrEqual): self
392
+	{
393
+		$this->logicalSeparatorForGreaterThanOrEqual = $logicalSeparatorForGreaterThanOrEqual;
394
+		return $this;
395
+	}
396
+
397
+	/**
398
+	 * @return string
399
+	 */
400
+	public function getLogicalSeparatorForLessThan(): string
401
+	{
402
+		return $this->logicalSeparatorForLessThan;
403
+	}
404
+
405
+	/**
406
+	 * @param string $logicalSeparatorForLessThan
407
+	 * @return $this
408
+	 */
409
+	public function setLogicalSeparatorForLessThan($logicalSeparatorForLessThan): self
410
+	{
411
+		$this->logicalSeparatorForLessThan = $logicalSeparatorForLessThan;
412
+		return $this;
413
+	}
414
+
415
+	/**
416
+	 * @return string
417
+	 */
418
+	public function getLogicalSeparatorForLessThanOrEqual(): string
419
+	{
420
+		return $this->logicalSeparatorForLessThanOrEqual;
421
+	}
422
+
423
+	/**
424
+	 * @param string $logicalSeparatorForLessThanOrEqual
425
+	 * @return $this
426
+	 */
427
+	public function setLogicalSeparatorForLessThanOrEqual($logicalSeparatorForLessThanOrEqual): self
428
+	{
429
+		$this->logicalSeparatorForLessThanOrEqual = $logicalSeparatorForLessThanOrEqual;
430
+		return $this;
431
+	}
432
+
433
+	/**
434
+	 * @return string
435
+	 */
436
+	public function getLogicalSeparatorForIn(): string
437
+	{
438
+		return $this->logicalSeparatorForIn;
439
+	}
440
+
441
+	/**
442
+	 * @param string $logicalSeparatorForIn
443
+	 * @return $this
444
+	 */
445
+	public function setLogicalSeparatorForIn($logicalSeparatorForIn): self
446
+	{
447
+		$this->logicalSeparatorForIn = $logicalSeparatorForIn;
448
+		return $this;
449
+	}
450
+
451
+	/**
452
+	 * @return string
453
+	 */
454
+	public function getLogicalSeparatorForLike(): string
455
+	{
456
+		return $this->logicalSeparatorForLike;
457
+	}
458
+
459
+	/**
460
+	 * @param string $logicalSeparatorForLike
461
+	 * @return $this
462
+	 */
463
+	public function setLogicalSeparatorForLike($logicalSeparatorForLike): self
464
+	{
465
+		$this->logicalSeparatorForLike = $logicalSeparatorForLike;
466
+		return $this;
467
+	}
468
+
469
+	/**
470
+	 * @return string
471
+	 */
472
+	public function getLogicalSeparatorForSearchTerm(): string
473
+	{
474
+		return $this->logicalSeparatorForSearchTerm;
475
+	}
476
+
477
+	/**
478
+	 * @param string $logicalSeparatorForSearchTerm
479
+	 * @return $this
480
+	 */
481
+	public function setLogicalSeparatorForSearchTerm($logicalSeparatorForSearchTerm): self
482
+	{
483
+		$this->logicalSeparatorForSearchTerm = $logicalSeparatorForSearchTerm;
484
+		return $this;
485
+	}
486
+
487
+	/**
488
+	 * @return array
489
+	 */
490
+	public function getSupportedOperators(): array
491
+	{
492
+		return $this->supportedOperators;
493
+	}
494
+
495
+	/**
496
+	 * @return string
497
+	 */
498
+	public function getDataType(): string
499
+	{
500
+		return $this->dataType;
501
+	}
502
+
503
+	/**
504
+	 * @param string $dataType
505
+	 * @return $this
506
+	 */
507
+	public function setDataType($dataType): self
508
+	{
509
+		$this->dataType = $dataType;
510
+		return $this;
511
+	}
512 512
 }
Please login to merge, or discard this patch.
Classes/Persistence/MatcherObjectFactory.php 1 patch
Indentation   +255 added lines, -255 removed lines patch added patch discarded remove patch
@@ -24,259 +24,259 @@
 block discarded – undo
24 24
  */
25 25
 class MatcherObjectFactory implements SingletonInterface
26 26
 {
27
-    /**
28
-     * Gets a singleton instance of this class.
29
-     *
30
-     * @return $this
31
-     */
32
-    public static function getInstance(): self
33
-    {
34
-        return GeneralUtility::makeInstance(self::class);
35
-    }
36
-
37
-    /**
38
-     * Returns a matcher object.
39
-     *
40
-     * @param array $matches
41
-     * @param string $dataType
42
-     * @return Matcher
43
-     */
44
-    public function getMatcher(array $matches = [], $dataType = ''): Matcher
45
-    {
46
-        if ($dataType === '') {
47
-            $dataType = $this->getModuleLoader()->getDataType();
48
-        }
49
-
50
-        /** @var $matcher Matcher */
51
-        $matcher = GeneralUtility::makeInstance(Matcher::class, [], $dataType);
52
-
53
-        $matcher = $this->applyCriteriaFromDataTables($matcher);
54
-        $matcher = $this->applyCriteriaFromMatchesArgument($matcher, $matches);
55
-
56
-        if ($this->isBackendMode()) {
57
-            $matcher = $this->applyCriteriaFromUrl($matcher);
58
-            $matcher = $this->applyCriteriaFromTSConfig($matcher);
59
-        }
60
-
61
-        // Trigger signal for post processing Matcher Object.
62
-        $this->emitPostProcessMatcherObjectSignal($matcher);
63
-
64
-        return $matcher;
65
-    }
66
-
67
-    /**
68
-     * Get a possible id from the URL and apply as filter criteria.
69
-     * Except if the main module belongs to the File. The id would be a combined identifier
70
-     * including the storage and a mount point.
71
-     *
72
-     * @param Matcher $matcher
73
-     * @return Matcher $matcher
74
-     */
75
-    protected function applyCriteriaFromUrl(Matcher $matcher): Matcher
76
-    {
77
-        if (GeneralUtility::_GP('id')
78
-            && !$this->getModuleLoader()->isPidIgnored()
79
-            && $this->getModuleLoader()->getMainModule() !== ModuleName::FILE) {
80
-            $matcher->equals('pid', GeneralUtility::_GP('id'));
81
-        }
82
-
83
-        return $matcher;
84
-    }
85
-
86
-    /**
87
-     * @param Matcher $matcher
88
-     * @return Matcher $matcher
89
-     */
90
-    protected function applyCriteriaFromTSConfig(Matcher $matcher): Matcher
91
-    {
92
-        $dataType = $matcher->getDataType();
93
-        $tsConfigPath = sprintf('tx_vidi.dataType.%s.constraints', $dataType);
94
-        $tsConfig = $this->getBackendUser()->getTSConfig($tsConfigPath);
95
-
96
-        if (is_array($tsConfig['properties']) && !empty($tsConfig['properties'])) {
97
-            foreach ($tsConfig['properties'] as $constraint) {
98
-                if (preg_match('/(.+) (>=|>|<|<=|=|like) (.+)/is', $constraint, $matches) && count($matches) === 4) {
99
-                    $operator = $matcher->getSupportedOperators()[strtolower(trim($matches[2]))];
100
-                    $operand = trim($matches[1]);
101
-                    $value = trim($matches[3]);
102
-
103
-                    $matcher->$operator($operand, $value);
104
-                } elseif (preg_match('/(.+) (in) (.+)/is', $constraint, $matches) && count($matches) === 4) {
105
-                    $operator = $matcher->getSupportedOperators()[trim($matches[2])];
106
-                    $operand = trim($matches[1]);
107
-                    $value = trim($matches[3]);
108
-                    $matcher->$operator($operand, GeneralUtility::trimExplode(',', $value, true));
109
-                }
110
-            }
111
-        }
112
-
113
-        return $matcher;
114
-    }
115
-
116
-    /**
117
-     * @param Matcher $matcher
118
-     * @param array $matches
119
-     * @return Matcher $matcher
120
-     */
121
-    protected function applyCriteriaFromMatchesArgument(Matcher $matcher, $matches): Matcher
122
-    {
123
-        foreach ($matches as $fieldNameAndPath => $value) {
124
-            // CSV values should be considered as "in" operator in the query, otherwise "equals".
125
-            $explodedValues = GeneralUtility::trimExplode(',', $value, true);
126
-            if (count($explodedValues) > 1) {
127
-                $matcher->in($fieldNameAndPath, $explodedValues);
128
-            } else {
129
-                $matcher->equals($fieldNameAndPath, $explodedValues[0]);
130
-            }
131
-        }
132
-
133
-        return $matcher;
134
-    }
135
-
136
-    /**
137
-     * Apply criteria specific to jQuery plugin DataTable.
138
-     *
139
-     * @param Matcher $matcher
140
-     * @return Matcher $matcher
141
-     */
142
-    protected function applyCriteriaFromDataTables(Matcher $matcher): Matcher
143
-    {
144
-        // Special case for Grid in the BE using jQuery DataTables plugin.
145
-        // Retrieve a possible search term from GP.
146
-        $query = GeneralUtility::_GP('search');
147
-        if (is_array($query)) {
148
-            if (!empty($query['value'])) {
149
-                $query = $query['value'];
150
-            } else {
151
-                $query = '';
152
-            }
153
-        }
154
-
155
-        if (strlen($query) > 0) {
156
-            // Parse the json query coming from the Visual Search.
157
-            $query = rawurldecode($query);
158
-            $queryParts = json_decode($query, true);
159
-
160
-            if (is_array($queryParts)) {
161
-                $matcher = $this->parseQuery($queryParts, $matcher);
162
-            } else {
163
-                $matcher->setSearchTerm($query);
164
-            }
165
-        }
166
-        return $matcher;
167
-    }
168
-
169
-    /**
170
-     * @param array $queryParts
171
-     * @param Matcher $matcher
172
-     * @return Matcher $matcher
173
-     */
174
-    protected function parseQuery(array $queryParts, Matcher $matcher): Matcher
175
-    {
176
-        $dataType = $matcher->getDataType();
177
-        foreach ($queryParts as $term) {
178
-            $fieldNameAndPath = key($term);
179
-
180
-            $resolvedDataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $dataType);
181
-            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $dataType);
182
-
183
-            // Retrieve the value.
184
-            $value = current($term);
185
-
186
-            if (Tca::grid($resolvedDataType)->hasFacet($fieldName) && Tca::grid($resolvedDataType)->facet($fieldName)->canModifyMatcher()) {
187
-                $matcher = Tca::grid($resolvedDataType)->facet($fieldName)->modifyMatcher($matcher, $value);
188
-            } elseif (Tca::table($resolvedDataType)->hasField($fieldName)) {
189
-                // Check whether the field exists and set it as "equal" or "like".
190
-                if ($this->isOperatorEquals($fieldNameAndPath, $dataType, $value)) {
191
-                    $matcher->equals($fieldNameAndPath, $value);
192
-                } else {
193
-                    $matcher->like($fieldNameAndPath, $value);
194
-                }
195
-            } elseif ($fieldNameAndPath === 'text') {
196
-                // Special case if field is "text" which is a pseudo field in this case.
197
-                // Set the search term which means Vidi will
198
-                // search in various fields with operator "like". The fields come from key "searchFields" in the TCA.
199
-                $matcher->setSearchTerm($value);
200
-            }
201
-        }
202
-        return $matcher;
203
-    }
204
-
205
-    /**
206
-     * Tell whether the operator should be equals instead of like for a search, e.g. if the value is numerical.
207
-     *
208
-     * @param string $fieldName
209
-     * @param string $dataType
210
-     * @param string $value
211
-     * @return bool
212
-     */
213
-    protected function isOperatorEquals($fieldName, $dataType, $value): bool
214
-    {
215
-        return (Tca::table($dataType)->field($fieldName)->hasRelation() && MathUtility::canBeInterpretedAsInteger($value))
216
-            || Tca::table($dataType)->field($fieldName)->isNumerical();
217
-    }
218
-
219
-    /**
220
-     * Signal that is called for post-processing a matcher object.
221
-     *
222
-     * @param Matcher $matcher
223
-     */
224
-    protected function emitPostProcessMatcherObjectSignal(Matcher $matcher): void
225
-    {
226
-        if (strlen($matcher->getDataType()) <= 0) {
227
-            /** @var ModuleLoader $moduleLoader */
228
-            $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
229
-            $matcher->setDataType($moduleLoader->getDataType());
230
-        }
231
-
232
-        $this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'postProcessMatcherObject', array($matcher, $matcher->getDataType()));
233
-    }
234
-
235
-    /**
236
-     * Get the SignalSlot dispatcher
237
-     *
238
-     * @return Dispatcher|object
239
-     */
240
-    protected function getSignalSlotDispatcher()
241
-    {
242
-        return GeneralUtility::makeInstance(Dispatcher::class);
243
-    }
244
-
245
-    /**
246
-     * Get the Vidi Module Loader.
247
-     *
248
-     * @return ModuleLoader|object
249
-     */
250
-    protected function getModuleLoader()
251
-    {
252
-        return GeneralUtility::makeInstance(ModuleLoader::class);
253
-    }
254
-
255
-    /**
256
-     * @return FieldPathResolver|object
257
-     */
258
-    protected function getFieldPathResolver()
259
-    {
260
-        return GeneralUtility::makeInstance(FieldPathResolver::class);
261
-    }
262
-
263
-    /**
264
-     * Returns an instance of the current Backend User.
265
-     *
266
-     * @return BackendUserAuthentication
267
-     */
268
-    protected function getBackendUser(): BackendUserAuthentication
269
-    {
270
-        return $GLOBALS['BE_USER'];
271
-    }
272
-
273
-    /**
274
-     * Returns whether the current mode is Backend
275
-     *
276
-     * @return bool
277
-     */
278
-    protected function isBackendMode(): bool
279
-    {
280
-        return ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend();
281
-    }
27
+	/**
28
+	 * Gets a singleton instance of this class.
29
+	 *
30
+	 * @return $this
31
+	 */
32
+	public static function getInstance(): self
33
+	{
34
+		return GeneralUtility::makeInstance(self::class);
35
+	}
36
+
37
+	/**
38
+	 * Returns a matcher object.
39
+	 *
40
+	 * @param array $matches
41
+	 * @param string $dataType
42
+	 * @return Matcher
43
+	 */
44
+	public function getMatcher(array $matches = [], $dataType = ''): Matcher
45
+	{
46
+		if ($dataType === '') {
47
+			$dataType = $this->getModuleLoader()->getDataType();
48
+		}
49
+
50
+		/** @var $matcher Matcher */
51
+		$matcher = GeneralUtility::makeInstance(Matcher::class, [], $dataType);
52
+
53
+		$matcher = $this->applyCriteriaFromDataTables($matcher);
54
+		$matcher = $this->applyCriteriaFromMatchesArgument($matcher, $matches);
55
+
56
+		if ($this->isBackendMode()) {
57
+			$matcher = $this->applyCriteriaFromUrl($matcher);
58
+			$matcher = $this->applyCriteriaFromTSConfig($matcher);
59
+		}
60
+
61
+		// Trigger signal for post processing Matcher Object.
62
+		$this->emitPostProcessMatcherObjectSignal($matcher);
63
+
64
+		return $matcher;
65
+	}
66
+
67
+	/**
68
+	 * Get a possible id from the URL and apply as filter criteria.
69
+	 * Except if the main module belongs to the File. The id would be a combined identifier
70
+	 * including the storage and a mount point.
71
+	 *
72
+	 * @param Matcher $matcher
73
+	 * @return Matcher $matcher
74
+	 */
75
+	protected function applyCriteriaFromUrl(Matcher $matcher): Matcher
76
+	{
77
+		if (GeneralUtility::_GP('id')
78
+			&& !$this->getModuleLoader()->isPidIgnored()
79
+			&& $this->getModuleLoader()->getMainModule() !== ModuleName::FILE) {
80
+			$matcher->equals('pid', GeneralUtility::_GP('id'));
81
+		}
82
+
83
+		return $matcher;
84
+	}
85
+
86
+	/**
87
+	 * @param Matcher $matcher
88
+	 * @return Matcher $matcher
89
+	 */
90
+	protected function applyCriteriaFromTSConfig(Matcher $matcher): Matcher
91
+	{
92
+		$dataType = $matcher->getDataType();
93
+		$tsConfigPath = sprintf('tx_vidi.dataType.%s.constraints', $dataType);
94
+		$tsConfig = $this->getBackendUser()->getTSConfig($tsConfigPath);
95
+
96
+		if (is_array($tsConfig['properties']) && !empty($tsConfig['properties'])) {
97
+			foreach ($tsConfig['properties'] as $constraint) {
98
+				if (preg_match('/(.+) (>=|>|<|<=|=|like) (.+)/is', $constraint, $matches) && count($matches) === 4) {
99
+					$operator = $matcher->getSupportedOperators()[strtolower(trim($matches[2]))];
100
+					$operand = trim($matches[1]);
101
+					$value = trim($matches[3]);
102
+
103
+					$matcher->$operator($operand, $value);
104
+				} elseif (preg_match('/(.+) (in) (.+)/is', $constraint, $matches) && count($matches) === 4) {
105
+					$operator = $matcher->getSupportedOperators()[trim($matches[2])];
106
+					$operand = trim($matches[1]);
107
+					$value = trim($matches[3]);
108
+					$matcher->$operator($operand, GeneralUtility::trimExplode(',', $value, true));
109
+				}
110
+			}
111
+		}
112
+
113
+		return $matcher;
114
+	}
115
+
116
+	/**
117
+	 * @param Matcher $matcher
118
+	 * @param array $matches
119
+	 * @return Matcher $matcher
120
+	 */
121
+	protected function applyCriteriaFromMatchesArgument(Matcher $matcher, $matches): Matcher
122
+	{
123
+		foreach ($matches as $fieldNameAndPath => $value) {
124
+			// CSV values should be considered as "in" operator in the query, otherwise "equals".
125
+			$explodedValues = GeneralUtility::trimExplode(',', $value, true);
126
+			if (count($explodedValues) > 1) {
127
+				$matcher->in($fieldNameAndPath, $explodedValues);
128
+			} else {
129
+				$matcher->equals($fieldNameAndPath, $explodedValues[0]);
130
+			}
131
+		}
132
+
133
+		return $matcher;
134
+	}
135
+
136
+	/**
137
+	 * Apply criteria specific to jQuery plugin DataTable.
138
+	 *
139
+	 * @param Matcher $matcher
140
+	 * @return Matcher $matcher
141
+	 */
142
+	protected function applyCriteriaFromDataTables(Matcher $matcher): Matcher
143
+	{
144
+		// Special case for Grid in the BE using jQuery DataTables plugin.
145
+		// Retrieve a possible search term from GP.
146
+		$query = GeneralUtility::_GP('search');
147
+		if (is_array($query)) {
148
+			if (!empty($query['value'])) {
149
+				$query = $query['value'];
150
+			} else {
151
+				$query = '';
152
+			}
153
+		}
154
+
155
+		if (strlen($query) > 0) {
156
+			// Parse the json query coming from the Visual Search.
157
+			$query = rawurldecode($query);
158
+			$queryParts = json_decode($query, true);
159
+
160
+			if (is_array($queryParts)) {
161
+				$matcher = $this->parseQuery($queryParts, $matcher);
162
+			} else {
163
+				$matcher->setSearchTerm($query);
164
+			}
165
+		}
166
+		return $matcher;
167
+	}
168
+
169
+	/**
170
+	 * @param array $queryParts
171
+	 * @param Matcher $matcher
172
+	 * @return Matcher $matcher
173
+	 */
174
+	protected function parseQuery(array $queryParts, Matcher $matcher): Matcher
175
+	{
176
+		$dataType = $matcher->getDataType();
177
+		foreach ($queryParts as $term) {
178
+			$fieldNameAndPath = key($term);
179
+
180
+			$resolvedDataType = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $dataType);
181
+			$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $dataType);
182
+
183
+			// Retrieve the value.
184
+			$value = current($term);
185
+
186
+			if (Tca::grid($resolvedDataType)->hasFacet($fieldName) && Tca::grid($resolvedDataType)->facet($fieldName)->canModifyMatcher()) {
187
+				$matcher = Tca::grid($resolvedDataType)->facet($fieldName)->modifyMatcher($matcher, $value);
188
+			} elseif (Tca::table($resolvedDataType)->hasField($fieldName)) {
189
+				// Check whether the field exists and set it as "equal" or "like".
190
+				if ($this->isOperatorEquals($fieldNameAndPath, $dataType, $value)) {
191
+					$matcher->equals($fieldNameAndPath, $value);
192
+				} else {
193
+					$matcher->like($fieldNameAndPath, $value);
194
+				}
195
+			} elseif ($fieldNameAndPath === 'text') {
196
+				// Special case if field is "text" which is a pseudo field in this case.
197
+				// Set the search term which means Vidi will
198
+				// search in various fields with operator "like". The fields come from key "searchFields" in the TCA.
199
+				$matcher->setSearchTerm($value);
200
+			}
201
+		}
202
+		return $matcher;
203
+	}
204
+
205
+	/**
206
+	 * Tell whether the operator should be equals instead of like for a search, e.g. if the value is numerical.
207
+	 *
208
+	 * @param string $fieldName
209
+	 * @param string $dataType
210
+	 * @param string $value
211
+	 * @return bool
212
+	 */
213
+	protected function isOperatorEquals($fieldName, $dataType, $value): bool
214
+	{
215
+		return (Tca::table($dataType)->field($fieldName)->hasRelation() && MathUtility::canBeInterpretedAsInteger($value))
216
+			|| Tca::table($dataType)->field($fieldName)->isNumerical();
217
+	}
218
+
219
+	/**
220
+	 * Signal that is called for post-processing a matcher object.
221
+	 *
222
+	 * @param Matcher $matcher
223
+	 */
224
+	protected function emitPostProcessMatcherObjectSignal(Matcher $matcher): void
225
+	{
226
+		if (strlen($matcher->getDataType()) <= 0) {
227
+			/** @var ModuleLoader $moduleLoader */
228
+			$moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
229
+			$matcher->setDataType($moduleLoader->getDataType());
230
+		}
231
+
232
+		$this->getSignalSlotDispatcher()->dispatch('Fab\Vidi\Controller\Backend\ContentController', 'postProcessMatcherObject', array($matcher, $matcher->getDataType()));
233
+	}
234
+
235
+	/**
236
+	 * Get the SignalSlot dispatcher
237
+	 *
238
+	 * @return Dispatcher|object
239
+	 */
240
+	protected function getSignalSlotDispatcher()
241
+	{
242
+		return GeneralUtility::makeInstance(Dispatcher::class);
243
+	}
244
+
245
+	/**
246
+	 * Get the Vidi Module Loader.
247
+	 *
248
+	 * @return ModuleLoader|object
249
+	 */
250
+	protected function getModuleLoader()
251
+	{
252
+		return GeneralUtility::makeInstance(ModuleLoader::class);
253
+	}
254
+
255
+	/**
256
+	 * @return FieldPathResolver|object
257
+	 */
258
+	protected function getFieldPathResolver()
259
+	{
260
+		return GeneralUtility::makeInstance(FieldPathResolver::class);
261
+	}
262
+
263
+	/**
264
+	 * Returns an instance of the current Backend User.
265
+	 *
266
+	 * @return BackendUserAuthentication
267
+	 */
268
+	protected function getBackendUser(): BackendUserAuthentication
269
+	{
270
+		return $GLOBALS['BE_USER'];
271
+	}
272
+
273
+	/**
274
+	 * Returns whether the current mode is Backend
275
+	 *
276
+	 * @return bool
277
+	 */
278
+	protected function isBackendMode(): bool
279
+	{
280
+		return ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend();
281
+	}
282 282
 }
Please login to merge, or discard this patch.
Classes/Persistence/Pager.php 1 patch
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -16,168 +16,168 @@
 block discarded – undo
16 16
  */
17 17
 class Pager
18 18
 {
19
-    /**
20
-     * Total amount of entries
21
-     *
22
-     * @var integer
23
-     */
24
-    protected $count;
25
-
26
-    /**
27
-     * Current offset
28
-     *
29
-     * @var integer
30
-     */
31
-    protected $offset;
32
-
33
-    /**
34
-     * Current page index
35
-     *
36
-     * @var integer
37
-     */
38
-    protected $page;
39
-
40
-    /**
41
-     * Number of items per page
42
-     *
43
-     * @var integer
44
-     */
45
-    protected $limit = 10;
46
-
47
-    /**
48
-     * Constructs a new Pager
49
-     */
50
-    public function __construct()
51
-    {
52
-        $this->page = 1;
53
-    }
54
-
55
-    /**
56
-     * Returns the total amount of entries
57
-     *
58
-     * @return int
59
-     */
60
-    public function getCount()
61
-    {
62
-        return $this->count;
63
-    }
64
-
65
-    /**
66
-     * Sets the total amount of entries
67
-     *
68
-     * @param int $count
69
-     */
70
-    public function setCount($count)
71
-    {
72
-        $this->count = $count;
73
-    }
74
-
75
-    /**
76
-     * Returns the current page index
77
-     *
78
-     * @return int
79
-     */
80
-    public function getPage()
81
-    {
82
-        return $this->page;
83
-    }
84
-
85
-    /**
86
-     * Sets the current page index
87
-     *
88
-     * @param int $page
89
-     */
90
-    public function setPage($page)
91
-    {
92
-        $this->page = $page;
93
-    }
94
-
95
-    /**
96
-     * Returns the current limit index
97
-     *
98
-     * @return int
99
-     */
100
-    public function getLimit()
101
-    {
102
-        return $this->limit;
103
-    }
104
-
105
-    /**
106
-     * Sets the current limit index
107
-     *
108
-     * @param int $limit
109
-     */
110
-    public function setLimit($limit)
111
-    {
112
-        $this->limit = $limit;
113
-    }
114
-
115
-    /**
116
-     * @return Array Items to display
117
-     */
118
-    public function getDisplayItems()
119
-    {
120
-        $last = $this->getLastPage();
121
-        if ($last == 1) {
122
-            return null;
123
-        }
124
-        $values = array();
125
-        for ($i = 1; $i <= $last; $i++) {
126
-            $values[] = array('key' => $i, 'value' => $i);
127
-        }
128
-        return $values;
129
-    }
130
-
131
-    /**
132
-     * @return int The last page index
133
-     */
134
-    public function getLastPage()
135
-    {
136
-        $last = intval($this->count / $this->limit);
137
-        if ($this->count % $this->limit > 0) {
138
-            $last++;
139
-        }
140
-        return $last;
141
-    }
142
-
143
-    /**
144
-     * @return int The previous page index. Minimum value is 1
145
-     */
146
-    public function getPreviousPage()
147
-    {
148
-        $prev = $this->page - 1;
149
-        if ($prev < 1) {
150
-            $prev = 1;
151
-        }
152
-        return $prev;
153
-    }
154
-
155
-    /**
156
-     * @return int The next page index. Maximum valus is the last page
157
-     */
158
-    public function getNextPage()
159
-    {
160
-        $next = $this->page + 1;
161
-        $last = $this->getLastPage();
162
-        if ($next > $last) {
163
-            $next = $last;
164
-        }
165
-        return $next;
166
-    }
167
-
168
-    /**
169
-     * @return int
170
-     */
171
-    public function getOffset()
172
-    {
173
-        return $this->offset;
174
-    }
175
-
176
-    /**
177
-     * @param int $offset
178
-     */
179
-    public function setOffset($offset)
180
-    {
181
-        $this->offset = $offset;
182
-    }
19
+	/**
20
+	 * Total amount of entries
21
+	 *
22
+	 * @var integer
23
+	 */
24
+	protected $count;
25
+
26
+	/**
27
+	 * Current offset
28
+	 *
29
+	 * @var integer
30
+	 */
31
+	protected $offset;
32
+
33
+	/**
34
+	 * Current page index
35
+	 *
36
+	 * @var integer
37
+	 */
38
+	protected $page;
39
+
40
+	/**
41
+	 * Number of items per page
42
+	 *
43
+	 * @var integer
44
+	 */
45
+	protected $limit = 10;
46
+
47
+	/**
48
+	 * Constructs a new Pager
49
+	 */
50
+	public function __construct()
51
+	{
52
+		$this->page = 1;
53
+	}
54
+
55
+	/**
56
+	 * Returns the total amount of entries
57
+	 *
58
+	 * @return int
59
+	 */
60
+	public function getCount()
61
+	{
62
+		return $this->count;
63
+	}
64
+
65
+	/**
66
+	 * Sets the total amount of entries
67
+	 *
68
+	 * @param int $count
69
+	 */
70
+	public function setCount($count)
71
+	{
72
+		$this->count = $count;
73
+	}
74
+
75
+	/**
76
+	 * Returns the current page index
77
+	 *
78
+	 * @return int
79
+	 */
80
+	public function getPage()
81
+	{
82
+		return $this->page;
83
+	}
84
+
85
+	/**
86
+	 * Sets the current page index
87
+	 *
88
+	 * @param int $page
89
+	 */
90
+	public function setPage($page)
91
+	{
92
+		$this->page = $page;
93
+	}
94
+
95
+	/**
96
+	 * Returns the current limit index
97
+	 *
98
+	 * @return int
99
+	 */
100
+	public function getLimit()
101
+	{
102
+		return $this->limit;
103
+	}
104
+
105
+	/**
106
+	 * Sets the current limit index
107
+	 *
108
+	 * @param int $limit
109
+	 */
110
+	public function setLimit($limit)
111
+	{
112
+		$this->limit = $limit;
113
+	}
114
+
115
+	/**
116
+	 * @return Array Items to display
117
+	 */
118
+	public function getDisplayItems()
119
+	{
120
+		$last = $this->getLastPage();
121
+		if ($last == 1) {
122
+			return null;
123
+		}
124
+		$values = array();
125
+		for ($i = 1; $i <= $last; $i++) {
126
+			$values[] = array('key' => $i, 'value' => $i);
127
+		}
128
+		return $values;
129
+	}
130
+
131
+	/**
132
+	 * @return int The last page index
133
+	 */
134
+	public function getLastPage()
135
+	{
136
+		$last = intval($this->count / $this->limit);
137
+		if ($this->count % $this->limit > 0) {
138
+			$last++;
139
+		}
140
+		return $last;
141
+	}
142
+
143
+	/**
144
+	 * @return int The previous page index. Minimum value is 1
145
+	 */
146
+	public function getPreviousPage()
147
+	{
148
+		$prev = $this->page - 1;
149
+		if ($prev < 1) {
150
+			$prev = 1;
151
+		}
152
+		return $prev;
153
+	}
154
+
155
+	/**
156
+	 * @return int The next page index. Maximum valus is the last page
157
+	 */
158
+	public function getNextPage()
159
+	{
160
+		$next = $this->page + 1;
161
+		$last = $this->getLastPage();
162
+		if ($next > $last) {
163
+			$next = $last;
164
+		}
165
+		return $next;
166
+	}
167
+
168
+	/**
169
+	 * @return int
170
+	 */
171
+	public function getOffset()
172
+	{
173
+		return $this->offset;
174
+	}
175
+
176
+	/**
177
+	 * @param int $offset
178
+	 */
179
+	public function setOffset($offset)
180
+	{
181
+		$this->offset = $offset;
182
+	}
183 183
 }
Please login to merge, or discard this patch.
Classes/Persistence/Order.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -14,45 +14,45 @@
 block discarded – undo
14 14
  */
15 15
 class Order
16 16
 {
17
-    /**
18
-     * The orderings
19
-     *
20
-     * @var array
21
-     */
22
-    protected $orderings = [];
17
+	/**
18
+	 * The orderings
19
+	 *
20
+	 * @var array
21
+	 */
22
+	protected $orderings = [];
23 23
 
24
-    /**
25
-     * Constructs a new Order
26
-     *
27
-     * @para array $orders
28
-     * @param array $orders
29
-     */
30
-    public function __construct($orders = array())
31
-    {
32
-        foreach ($orders as $order => $direction) {
33
-            $this->addOrdering($order, $direction);
34
-        }
35
-    }
24
+	/**
25
+	 * Constructs a new Order
26
+	 *
27
+	 * @para array $orders
28
+	 * @param array $orders
29
+	 */
30
+	public function __construct($orders = array())
31
+	{
32
+		foreach ($orders as $order => $direction) {
33
+			$this->addOrdering($order, $direction);
34
+		}
35
+	}
36 36
 
37
-    /**
38
-     * Add ordering
39
-     *
40
-     * @param string $order The order
41
-     * @param string $direction ASC / DESC
42
-     * @return void
43
-     */
44
-    public function addOrdering($order, $direction)
45
-    {
46
-        $this->orderings[$order] = $direction;
47
-    }
37
+	/**
38
+	 * Add ordering
39
+	 *
40
+	 * @param string $order The order
41
+	 * @param string $direction ASC / DESC
42
+	 * @return void
43
+	 */
44
+	public function addOrdering($order, $direction)
45
+	{
46
+		$this->orderings[$order] = $direction;
47
+	}
48 48
 
49
-    /**
50
-     * Returns the order
51
-     *
52
-     * @return array The order
53
-     */
54
-    public function getOrderings()
55
-    {
56
-        return $this->orderings;
57
-    }
49
+	/**
50
+	 * Returns the order
51
+	 *
52
+	 * @return array The order
53
+	 */
54
+	public function getOrderings()
55
+	{
56
+		return $this->orderings;
57
+	}
58 58
 }
Please login to merge, or discard this patch.
Classes/Persistence/ResultSetStorage.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -16,31 +16,31 @@
 block discarded – undo
16 16
  */
17 17
 class ResultSetStorage implements SingletonInterface
18 18
 {
19
-    /**
20
-     * @var array
21
-     */
22
-    protected $resultSets = [];
19
+	/**
20
+	 * @var array
21
+	 */
22
+	protected $resultSets = [];
23 23
 
24
-    /**
25
-     * @param string $querySignature
26
-     * @return array
27
-     */
28
-    public function get($querySignature)
29
-    {
30
-        $resultSet = null;
31
-        if (isset($this->resultSets[$querySignature])) {
32
-            $resultSet = $this->resultSets[$querySignature];
33
-        }
34
-        return $resultSet;
35
-    }
24
+	/**
25
+	 * @param string $querySignature
26
+	 * @return array
27
+	 */
28
+	public function get($querySignature)
29
+	{
30
+		$resultSet = null;
31
+		if (isset($this->resultSets[$querySignature])) {
32
+			$resultSet = $this->resultSets[$querySignature];
33
+		}
34
+		return $resultSet;
35
+	}
36 36
 
37
-    /**
38
-     * @param $querySignature
39
-     * @param array $resultSet
40
-     * @internal param array $resultSets
41
-     */
42
-    public function set($querySignature, array $resultSet)
43
-    {
44
-        $this->resultSets[$querySignature] = $resultSet;
45
-    }
37
+	/**
38
+	 * @param $querySignature
39
+	 * @param array $resultSet
40
+	 * @internal param array $resultSets
41
+	 */
42
+	public function set($querySignature, array $resultSet)
43
+	{
44
+		$this->resultSets[$querySignature] = $resultSet;
45
+	}
46 46
 }
Please login to merge, or discard this patch.
Classes/Persistence/Query.php 1 patch
Indentation   +622 added lines, -622 removed lines patch added patch discarded remove patch
@@ -41,626 +41,626 @@
 block discarded – undo
41 41
  */
42 42
 class Query implements QueryInterface
43 43
 {
44
-    /**
45
-     * An inner join.
46
-     */
47
-    public const JCR_JOIN_TYPE_INNER = '{http://www.jcp.org/jcr/1.0}joinTypeInner';
48
-
49
-    /**
50
-     * A left-outer join.
51
-     */
52
-    public const JCR_JOIN_TYPE_LEFT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeLeftOuter';
53
-
54
-    /**
55
-     * A right-outer join.
56
-     */
57
-    public const JCR_JOIN_TYPE_RIGHT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeRightOuter';
58
-
59
-    /**
60
-     * Charset of strings in QOM
61
-     */
62
-    public const CHARSET = 'utf-8';
63
-
64
-    /**
65
-     * @var string
66
-     */
67
-    protected $sourceFieldName;
68
-
69
-    /**
70
-     * @var string
71
-     */
72
-    protected $type;
73
-
74
-    /**
75
-     * @var PersistenceManagerInterface
76
-     */
77
-    protected $persistenceManager;
78
-
79
-    /**
80
-     * @var QueryObjectModelFactory
81
-     */
82
-    protected $qomFactory;
83
-
84
-    /**
85
-     * @var SourceInterface
86
-     */
87
-    protected $source;
88
-
89
-    /**
90
-     * @var ConstraintInterface
91
-     */
92
-    protected $constraint;
93
-
94
-    /**
95
-     * @var Statement
96
-     */
97
-    protected $statement;
98
-
99
-    /**
100
-     * @var array
101
-     */
102
-    protected $orderings = [];
103
-
104
-    /**
105
-     * @var int
106
-     */
107
-    protected $limit;
108
-
109
-    /**
110
-     * @var int
111
-     */
112
-    protected $offset;
113
-
114
-    /**
115
-     * Apply DISTINCT upon property.
116
-     *
117
-     * @var string
118
-     */
119
-    protected $distinct;
120
-
121
-    /**
122
-     * The query settings.
123
-     *
124
-     * @var Typo3QuerySettings
125
-     */
126
-    public Typo3QuerySettings $typo3QuerySettings;
127
-
128
-    /**
129
-     * Constructs a query object working on the given class name
130
-     *
131
-     * @param string $type
132
-     */
133
-    public function __construct($type)
134
-    {
135
-        $this->type = $type;
136
-        $this->persistenceManager = GeneralUtility::makeInstance(PersistenceManagerInterface::class);
137
-        $this->qomFactory = GeneralUtility::makeInstance(QueryObjectModelFactory::class);
138
-    }
139
-
140
-    public function injectTypo3QuerySettings(Typo3QuerySettings $querySettings): void
141
-    {
142
-        $this->typo3QuerySettings = $querySettings;
143
-    }
144
-
145
-    /**
146
-     * Sets the Query Settings. These Query settings must match the settings expected by
147
-     * the specific Storage Backend.
148
-     *
149
-     * @param QuerySettingsInterface $typo3QuerySettings The Query Settings
150
-     * @return void
151
-     */
152
-    public function setTypo3QuerySettings(QuerySettingsInterface $typo3QuerySettings)
153
-    {
154
-        $this->typo3QuerySettings = $typo3QuerySettings;
155
-    }
156
-
157
-    /**
158
-     * Returns the Query Settings.
159
-     *
160
-     * @throws \Exception
161
-     * @return Typo3QuerySettings $querySettings The Query Settings
162
-     * @api This method is not part of FLOW3 API
163
-     */
164
-    public function getTypo3QuerySettings()
165
-    {
166
-        if (!$this->typo3QuerySettings instanceof QuerySettingsInterface) {
167
-            throw new Exception('Tried to get the query settings without setting them before.', 1248689115);
168
-        }
169
-
170
-        // Apply possible settings to the query.
171
-        if ($this->isBackendMode()) {
172
-            /** @var BackendConfigurationManager $backendConfigurationManager */
173
-            $backendConfigurationManager = GeneralUtility::makeInstance(BackendConfigurationManager::class);
174
-            $configuration = $backendConfigurationManager->getTypoScriptSetup();
175
-            $querySettings = array('respectSysLanguage');
176
-            foreach ($querySettings as $setting) {
177
-                if (isset($configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting])) {
178
-                    $value = (bool)$configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting];
179
-                    ObjectAccess::setProperty($this->typo3QuerySettings, $setting, $value);
180
-                }
181
-            }
182
-        }
183
-
184
-        return $this->typo3QuerySettings;
185
-    }
186
-
187
-    /**
188
-     * Returns the type this query cares for.
189
-     *
190
-     * @return string
191
-     * @api
192
-     */
193
-    public function getType()
194
-    {
195
-        return $this->type;
196
-    }
197
-
198
-    /**
199
-     * Sets the source to fetch the result from
200
-     *
201
-     * @param SourceInterface $source
202
-     */
203
-    public function setSource(SourceInterface $source)
204
-    {
205
-        $this->source = $source;
206
-    }
207
-
208
-    /**
209
-     * Returns the selectorn name or an empty string, if the source is not a selector
210
-     * TODO This has to be checked at another place
211
-     *
212
-     * @return string The selector name
213
-     */
214
-    protected function getSelectorName()
215
-    {
216
-        if ($this->getSource() instanceof SelectorInterface) {
217
-            return $this->source->getSelectorName();
218
-        } else {
219
-            return '';
220
-        }
221
-    }
222
-
223
-    /**
224
-     * Gets the node-tuple source for this query.
225
-     *
226
-     * @return SourceInterface the node-tuple source; non-null
227
-     */
228
-    public function getSource()
229
-    {
230
-        if ($this->source === null) {
231
-            $this->source = $this->qomFactory->selector($this->getType());
232
-        }
233
-        return $this->source;
234
-    }
235
-
236
-    /**
237
-     * Executes the query against the database and returns the result
238
-     *
239
-     * @return QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is true
240
-     * @api
241
-     */
242
-    public function execute($returnRawQueryResult = false)
243
-    {
244
-        /** @var VidiDbBackend $backend */
245
-        $backend = GeneralUtility::makeInstance(VidiDbBackend::class, $this);
246
-        return $backend->fetchResult();
247
-    }
248
-
249
-    /**
250
-     * Sets the property names to order the result by. Expected like this:
251
-     * array(
252
-     * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
253
-     * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
254
-     * )
255
-     * where 'foo' and 'bar' are property names.
256
-     *
257
-     * @param array $orderings The property names to order by
258
-     * @return QueryInterface
259
-     * @api
260
-     */
261
-    public function setOrderings(array $orderings)
262
-    {
263
-        $this->orderings = $orderings;
264
-        return $this;
265
-    }
266
-
267
-    /**
268
-     * Returns the property names to order the result by. Like this:
269
-     * array(
270
-     * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
271
-     * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
272
-     * )
273
-     *
274
-     * @return array
275
-     */
276
-    public function getOrderings()
277
-    {
278
-        return $this->orderings;
279
-    }
280
-
281
-    /**
282
-     * Sets the maximum size of the result set to limit. Returns $this to allow
283
-     * for chaining (fluid interface)
284
-     *
285
-     * @param integer $limit
286
-     * @throws \InvalidArgumentException
287
-     * @return QueryInterface
288
-     * @api
289
-     */
290
-    public function setLimit($limit)
291
-    {
292
-        if (!is_int($limit) || $limit < 1) {
293
-            throw new \InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
294
-        }
295
-        $this->limit = $limit;
296
-        return $this;
297
-    }
298
-
299
-    /**
300
-     * Resets a previously set maximum size of the result set. Returns $this to allow
301
-     * for chaining (fluid interface)
302
-     *
303
-     * @return QueryInterface
304
-     * @api
305
-     */
306
-    public function unsetLimit()
307
-    {
308
-        unset($this->limit);
309
-        return $this;
310
-    }
311
-
312
-    /**
313
-     * Returns the maximum size of the result set to limit.
314
-     *
315
-     * @return integer
316
-     * @api
317
-     */
318
-    public function getLimit()
319
-    {
320
-        return $this->limit;
321
-    }
322
-
323
-    /**
324
-     * Sets the start offset of the result set to offset. Returns $this to
325
-     * allow for chaining (fluid interface)
326
-     *
327
-     * @param integer $offset
328
-     * @throws \InvalidArgumentException
329
-     * @return QueryInterface
330
-     * @api
331
-     */
332
-    public function setOffset($offset)
333
-    {
334
-        if (!is_int($offset) || $offset < 0) {
335
-            throw new \InvalidArgumentException('The offset must be a positive integer', 1245071872);
336
-        }
337
-        $this->offset = $offset;
338
-        return $this;
339
-    }
340
-
341
-    /**
342
-     * Returns the start offset of the result set.
343
-     *
344
-     * @return integer
345
-     * @api
346
-     */
347
-    public function getOffset()
348
-    {
349
-        return $this->offset;
350
-    }
351
-
352
-    /**
353
-     * The constraint used to limit the result set. Returns $this to allow
354
-     * for chaining (fluid interface)
355
-     *
356
-     * @param ConstraintInterface $constraint
357
-     * @return QueryInterface
358
-     * @api
359
-     */
360
-    public function matching($constraint)
361
-    {
362
-        $this->constraint = $constraint;
363
-        return $this;
364
-    }
365
-
366
-    /**
367
-     * Gets the constraint for this query.
368
-     *
369
-     * @return ConstraintInterface the constraint, or null if none
370
-     * @api
371
-     */
372
-    public function getConstraint()
373
-    {
374
-        return $this->constraint;
375
-    }
376
-
377
-    /**
378
-     * Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND.
379
-     * It also scepts a single array of constraints to be concatenated.
380
-     *
381
-     * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
382
-     * @throws InvalidNumberOfConstraintsException
383
-     * @return AndInterface
384
-     * @api
385
-     */
386
-    public function logicalAnd($constraint1)
387
-    {
388
-        if (is_array($constraint1)) {
389
-            $resultingConstraint = array_shift($constraint1);
390
-            $constraints = $constraint1;
391
-        } else {
392
-            $constraints = func_get_args();
393
-            $resultingConstraint = array_shift($constraints);
394
-        }
395
-        if ($resultingConstraint === null) {
396
-            throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289500);
397
-        }
398
-        foreach ($constraints as $constraint) {
399
-            $resultingConstraint = $this->qomFactory->_and($resultingConstraint, $constraint);
400
-        }
401
-        return $resultingConstraint;
402
-    }
403
-
404
-    /**
405
-     * Performs a logical disjunction of the two given constraints
406
-     *
407
-     * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
408
-     * @throws InvalidNumberOfConstraintsException
409
-     * @return OrInterface
410
-     * @api
411
-     */
412
-    public function logicalOr($constraint1)
413
-    {
414
-        if (is_array($constraint1)) {
415
-            $resultingConstraint = array_shift($constraint1);
416
-            $constraints = $constraint1;
417
-        } else {
418
-            $constraints = func_get_args();
419
-            $resultingConstraint = array_shift($constraints);
420
-        }
421
-        if ($resultingConstraint === null) {
422
-            throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289501);
423
-        }
424
-        foreach ($constraints as $constraint) {
425
-            $resultingConstraint = $this->qomFactory->_or($resultingConstraint, $constraint);
426
-        }
427
-        return $resultingConstraint;
428
-    }
429
-
430
-    /**
431
-     * Performs a logical negation of the given constraint
432
-     *
433
-     * @param ConstraintInterface $constraint Constraint to negate
434
-     * @throws \RuntimeException
435
-     * @return NotInterface
436
-     * @api
437
-     */
438
-    public function logicalNot(ConstraintInterface $constraint)
439
-    {
440
-        return $this->qomFactory->not($constraint);
441
-    }
442
-
443
-    /**
444
-     * Returns an equals criterion used for matching objects against a query
445
-     *
446
-     * @param string $propertyName The name of the property to compare against
447
-     * @param mixed $operand The value to compare with
448
-     * @param boolean $caseSensitive Whether the equality test should be done case-sensitive
449
-     * @return ComparisonInterface
450
-     * @api
451
-     */
452
-    public function equals($propertyName, $operand, $caseSensitive = true)
453
-    {
454
-        if (is_object($operand) || $caseSensitive) {
455
-            $comparison = $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_EQUAL_TO, $operand);
456
-        } else {
457
-            $comparison = $this->qomFactory->comparison($this->qomFactory->lowerCase($this->qomFactory->propertyValue($propertyName, $this->getSelectorName())), QueryInterface::OPERATOR_EQUAL_TO, mb_strtolower($operand, \TYPO3\CMS\Extbase\Persistence\Generic\Query::CHARSET));
458
-        }
459
-        return $comparison;
460
-    }
461
-
462
-    /**
463
-     * Returns a like criterion used for matching objects against a query
464
-     *
465
-     * @param string $propertyName The name of the property to compare against
466
-     * @param mixed $operand The value to compare with
467
-     * @param boolean $caseSensitive Whether the matching should be done case-sensitive
468
-     * @return ComparisonInterface
469
-     * @api
470
-     */
471
-    public function like($propertyName, $operand, $caseSensitive = true)
472
-    {
473
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LIKE, $operand);
474
-    }
475
-
476
-    /**
477
-     * Returns a "contains" criterion used for matching objects against a query.
478
-     * It matches if the multivalued property contains the given operand.
479
-     *
480
-     * @param string $propertyName The name of the (multivalued) property to compare against
481
-     * @param mixed $operand The value to compare with
482
-     * @return ComparisonInterface
483
-     * @api
484
-     */
485
-    public function contains($propertyName, $operand)
486
-    {
487
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_CONTAINS, $operand);
488
-    }
489
-
490
-    /**
491
-     * Returns an "in" criterion used for matching objects against a query. It
492
-     * matches if the property's value is contained in the multivalued operand.
493
-     *
494
-     * @param string $propertyName The name of the property to compare against
495
-     * @param mixed $operand The value to compare with, multivalued
496
-     * @throws UnexpectedTypeException
497
-     * @return ComparisonInterface
498
-     * @api
499
-     */
500
-    public function in($propertyName, $operand)
501
-    {
502
-        if (!is_array($operand) && !$operand instanceof \ArrayAccess && !$operand instanceof \Traversable) {
503
-            throw new UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
504
-        }
505
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_IN, $operand);
506
-    }
507
-
508
-    /**
509
-     * Returns a less than criterion used for matching objects against a query
510
-     *
511
-     * @param string $propertyName The name of the property to compare against
512
-     * @param mixed $operand The value to compare with
513
-     * @return ComparisonInterface
514
-     * @api
515
-     */
516
-    public function lessThan($propertyName, $operand)
517
-    {
518
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN, $operand);
519
-    }
520
-
521
-    /**
522
-     * Returns a less or equal than criterion used for matching objects against a query
523
-     *
524
-     * @param string $propertyName The name of the property to compare against
525
-     * @param mixed $operand The value to compare with
526
-     * @return ComparisonInterface
527
-     * @api
528
-     */
529
-    public function lessThanOrEqual($propertyName, $operand)
530
-    {
531
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO, $operand);
532
-    }
533
-
534
-    /**
535
-     * Returns a greater than criterion used for matching objects against a query
536
-     *
537
-     * @param string $propertyName The name of the property to compare against
538
-     * @param mixed $operand The value to compare with
539
-     * @return ComparisonInterface
540
-     * @api
541
-     */
542
-    public function greaterThan($propertyName, $operand)
543
-    {
544
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN, $operand);
545
-    }
546
-
547
-    /**
548
-     * Returns a greater than or equal criterion used for matching objects against a query
549
-     *
550
-     * @param string $propertyName The name of the property to compare against
551
-     * @param mixed $operand The value to compare with
552
-     * @return ComparisonInterface
553
-     * @api
554
-     */
555
-    public function greaterThanOrEqual($propertyName, $operand)
556
-    {
557
-        return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $operand);
558
-    }
559
-
560
-    /**
561
-     * Returns the query result count.
562
-     *
563
-     * @return integer The query result count
564
-     * @api
565
-     */
566
-    public function count()
567
-    {
568
-        /** @var VidiDbBackend $backend */
569
-        $backend = GeneralUtility::makeInstance(VidiDbBackend::class, $this);
570
-        return $backend->countResult();
571
-    }
572
-
573
-    /**
574
-     * Returns an "isEmpty" criterion used for matching objects against a query.
575
-     * It matches if the multivalued property contains no values or is null.
576
-     *
577
-     * @param string $propertyName The name of the multivalued property to compare against
578
-     * @throws NotImplementedException
579
-     * @throws InvalidQueryException if used on a single-valued property
580
-     * @api
581
-     */
582
-    public function isEmpty($propertyName)
583
-    {
584
-        throw new NotImplementedException(__METHOD__);
585
-    }
586
-
587
-    /**
588
-     * @return string
589
-     */
590
-    public function getDistinct()
591
-    {
592
-        return $this->distinct;
593
-    }
594
-
595
-    /**
596
-     * @param string $distinct
597
-     * @return $this
598
-     */
599
-    public function setDistinct($distinct)
600
-    {
601
-        $this->distinct = $distinct;
602
-        return $this;
603
-    }
604
-
605
-    /**
606
-     * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
607
-     * backend (database).
608
-     *
609
-     * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement
610
-     * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
611
-     * @return QueryInterface
612
-     */
613
-    public function statement($statement, array $parameters = array())
614
-    {
615
-        $this->statement = $this->qomFactory->statement($statement, $parameters);
616
-        return $this;
617
-    }
618
-
619
-    /**
620
-     * Returns the statement of this query.
621
-     *
622
-     * @return Statement
623
-     */
624
-    public function getStatement()
625
-    {
626
-        return $this->statement;
627
-    }
628
-
629
-    /**
630
-     * Returns whether the current mode is Backend.
631
-     *
632
-     * @return bool
633
-     */
634
-    protected function isBackendMode()
635
-    {
636
-        return ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend();
637
-    }
638
-
639
-    /**
640
-     * @return string
641
-     */
642
-    public function getSourceFieldName()
643
-    {
644
-        return $this->sourceFieldName;
645
-    }
646
-
647
-    /**
648
-     * @param string $sourceFieldName
649
-     * @return $this
650
-     */
651
-    public function setSourceFieldName($sourceFieldName)
652
-    {
653
-        $this->sourceFieldName = $sourceFieldName;
654
-        return $this;
655
-    }
656
-
657
-    public function setQuerySettings(QuerySettingsInterface $querySettings)
658
-    {
659
-        $this->typo3QuerySettings = $querySettings;
660
-    }
661
-
662
-    public function getQuerySettings()
663
-    {
664
-        return $this->typo3QuerySettings;
665
-    }
44
+	/**
45
+	 * An inner join.
46
+	 */
47
+	public const JCR_JOIN_TYPE_INNER = '{http://www.jcp.org/jcr/1.0}joinTypeInner';
48
+
49
+	/**
50
+	 * A left-outer join.
51
+	 */
52
+	public const JCR_JOIN_TYPE_LEFT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeLeftOuter';
53
+
54
+	/**
55
+	 * A right-outer join.
56
+	 */
57
+	public const JCR_JOIN_TYPE_RIGHT_OUTER = '{http://www.jcp.org/jcr/1.0}joinTypeRightOuter';
58
+
59
+	/**
60
+	 * Charset of strings in QOM
61
+	 */
62
+	public const CHARSET = 'utf-8';
63
+
64
+	/**
65
+	 * @var string
66
+	 */
67
+	protected $sourceFieldName;
68
+
69
+	/**
70
+	 * @var string
71
+	 */
72
+	protected $type;
73
+
74
+	/**
75
+	 * @var PersistenceManagerInterface
76
+	 */
77
+	protected $persistenceManager;
78
+
79
+	/**
80
+	 * @var QueryObjectModelFactory
81
+	 */
82
+	protected $qomFactory;
83
+
84
+	/**
85
+	 * @var SourceInterface
86
+	 */
87
+	protected $source;
88
+
89
+	/**
90
+	 * @var ConstraintInterface
91
+	 */
92
+	protected $constraint;
93
+
94
+	/**
95
+	 * @var Statement
96
+	 */
97
+	protected $statement;
98
+
99
+	/**
100
+	 * @var array
101
+	 */
102
+	protected $orderings = [];
103
+
104
+	/**
105
+	 * @var int
106
+	 */
107
+	protected $limit;
108
+
109
+	/**
110
+	 * @var int
111
+	 */
112
+	protected $offset;
113
+
114
+	/**
115
+	 * Apply DISTINCT upon property.
116
+	 *
117
+	 * @var string
118
+	 */
119
+	protected $distinct;
120
+
121
+	/**
122
+	 * The query settings.
123
+	 *
124
+	 * @var Typo3QuerySettings
125
+	 */
126
+	public Typo3QuerySettings $typo3QuerySettings;
127
+
128
+	/**
129
+	 * Constructs a query object working on the given class name
130
+	 *
131
+	 * @param string $type
132
+	 */
133
+	public function __construct($type)
134
+	{
135
+		$this->type = $type;
136
+		$this->persistenceManager = GeneralUtility::makeInstance(PersistenceManagerInterface::class);
137
+		$this->qomFactory = GeneralUtility::makeInstance(QueryObjectModelFactory::class);
138
+	}
139
+
140
+	public function injectTypo3QuerySettings(Typo3QuerySettings $querySettings): void
141
+	{
142
+		$this->typo3QuerySettings = $querySettings;
143
+	}
144
+
145
+	/**
146
+	 * Sets the Query Settings. These Query settings must match the settings expected by
147
+	 * the specific Storage Backend.
148
+	 *
149
+	 * @param QuerySettingsInterface $typo3QuerySettings The Query Settings
150
+	 * @return void
151
+	 */
152
+	public function setTypo3QuerySettings(QuerySettingsInterface $typo3QuerySettings)
153
+	{
154
+		$this->typo3QuerySettings = $typo3QuerySettings;
155
+	}
156
+
157
+	/**
158
+	 * Returns the Query Settings.
159
+	 *
160
+	 * @throws \Exception
161
+	 * @return Typo3QuerySettings $querySettings The Query Settings
162
+	 * @api This method is not part of FLOW3 API
163
+	 */
164
+	public function getTypo3QuerySettings()
165
+	{
166
+		if (!$this->typo3QuerySettings instanceof QuerySettingsInterface) {
167
+			throw new Exception('Tried to get the query settings without setting them before.', 1248689115);
168
+		}
169
+
170
+		// Apply possible settings to the query.
171
+		if ($this->isBackendMode()) {
172
+			/** @var BackendConfigurationManager $backendConfigurationManager */
173
+			$backendConfigurationManager = GeneralUtility::makeInstance(BackendConfigurationManager::class);
174
+			$configuration = $backendConfigurationManager->getTypoScriptSetup();
175
+			$querySettings = array('respectSysLanguage');
176
+			foreach ($querySettings as $setting) {
177
+				if (isset($configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting])) {
178
+					$value = (bool)$configuration['config.']['tx_vidi.']['persistence.']['backend.'][$this->type . '.'][$setting];
179
+					ObjectAccess::setProperty($this->typo3QuerySettings, $setting, $value);
180
+				}
181
+			}
182
+		}
183
+
184
+		return $this->typo3QuerySettings;
185
+	}
186
+
187
+	/**
188
+	 * Returns the type this query cares for.
189
+	 *
190
+	 * @return string
191
+	 * @api
192
+	 */
193
+	public function getType()
194
+	{
195
+		return $this->type;
196
+	}
197
+
198
+	/**
199
+	 * Sets the source to fetch the result from
200
+	 *
201
+	 * @param SourceInterface $source
202
+	 */
203
+	public function setSource(SourceInterface $source)
204
+	{
205
+		$this->source = $source;
206
+	}
207
+
208
+	/**
209
+	 * Returns the selectorn name or an empty string, if the source is not a selector
210
+	 * TODO This has to be checked at another place
211
+	 *
212
+	 * @return string The selector name
213
+	 */
214
+	protected function getSelectorName()
215
+	{
216
+		if ($this->getSource() instanceof SelectorInterface) {
217
+			return $this->source->getSelectorName();
218
+		} else {
219
+			return '';
220
+		}
221
+	}
222
+
223
+	/**
224
+	 * Gets the node-tuple source for this query.
225
+	 *
226
+	 * @return SourceInterface the node-tuple source; non-null
227
+	 */
228
+	public function getSource()
229
+	{
230
+		if ($this->source === null) {
231
+			$this->source = $this->qomFactory->selector($this->getType());
232
+		}
233
+		return $this->source;
234
+	}
235
+
236
+	/**
237
+	 * Executes the query against the database and returns the result
238
+	 *
239
+	 * @return QueryResultInterface|array The query result object or an array if $this->getQuerySettings()->getReturnRawQueryResult() is true
240
+	 * @api
241
+	 */
242
+	public function execute($returnRawQueryResult = false)
243
+	{
244
+		/** @var VidiDbBackend $backend */
245
+		$backend = GeneralUtility::makeInstance(VidiDbBackend::class, $this);
246
+		return $backend->fetchResult();
247
+	}
248
+
249
+	/**
250
+	 * Sets the property names to order the result by. Expected like this:
251
+	 * array(
252
+	 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
253
+	 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
254
+	 * )
255
+	 * where 'foo' and 'bar' are property names.
256
+	 *
257
+	 * @param array $orderings The property names to order by
258
+	 * @return QueryInterface
259
+	 * @api
260
+	 */
261
+	public function setOrderings(array $orderings)
262
+	{
263
+		$this->orderings = $orderings;
264
+		return $this;
265
+	}
266
+
267
+	/**
268
+	 * Returns the property names to order the result by. Like this:
269
+	 * array(
270
+	 * 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
271
+	 * 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
272
+	 * )
273
+	 *
274
+	 * @return array
275
+	 */
276
+	public function getOrderings()
277
+	{
278
+		return $this->orderings;
279
+	}
280
+
281
+	/**
282
+	 * Sets the maximum size of the result set to limit. Returns $this to allow
283
+	 * for chaining (fluid interface)
284
+	 *
285
+	 * @param integer $limit
286
+	 * @throws \InvalidArgumentException
287
+	 * @return QueryInterface
288
+	 * @api
289
+	 */
290
+	public function setLimit($limit)
291
+	{
292
+		if (!is_int($limit) || $limit < 1) {
293
+			throw new \InvalidArgumentException('The limit must be an integer >= 1', 1245071870);
294
+		}
295
+		$this->limit = $limit;
296
+		return $this;
297
+	}
298
+
299
+	/**
300
+	 * Resets a previously set maximum size of the result set. Returns $this to allow
301
+	 * for chaining (fluid interface)
302
+	 *
303
+	 * @return QueryInterface
304
+	 * @api
305
+	 */
306
+	public function unsetLimit()
307
+	{
308
+		unset($this->limit);
309
+		return $this;
310
+	}
311
+
312
+	/**
313
+	 * Returns the maximum size of the result set to limit.
314
+	 *
315
+	 * @return integer
316
+	 * @api
317
+	 */
318
+	public function getLimit()
319
+	{
320
+		return $this->limit;
321
+	}
322
+
323
+	/**
324
+	 * Sets the start offset of the result set to offset. Returns $this to
325
+	 * allow for chaining (fluid interface)
326
+	 *
327
+	 * @param integer $offset
328
+	 * @throws \InvalidArgumentException
329
+	 * @return QueryInterface
330
+	 * @api
331
+	 */
332
+	public function setOffset($offset)
333
+	{
334
+		if (!is_int($offset) || $offset < 0) {
335
+			throw new \InvalidArgumentException('The offset must be a positive integer', 1245071872);
336
+		}
337
+		$this->offset = $offset;
338
+		return $this;
339
+	}
340
+
341
+	/**
342
+	 * Returns the start offset of the result set.
343
+	 *
344
+	 * @return integer
345
+	 * @api
346
+	 */
347
+	public function getOffset()
348
+	{
349
+		return $this->offset;
350
+	}
351
+
352
+	/**
353
+	 * The constraint used to limit the result set. Returns $this to allow
354
+	 * for chaining (fluid interface)
355
+	 *
356
+	 * @param ConstraintInterface $constraint
357
+	 * @return QueryInterface
358
+	 * @api
359
+	 */
360
+	public function matching($constraint)
361
+	{
362
+		$this->constraint = $constraint;
363
+		return $this;
364
+	}
365
+
366
+	/**
367
+	 * Gets the constraint for this query.
368
+	 *
369
+	 * @return ConstraintInterface the constraint, or null if none
370
+	 * @api
371
+	 */
372
+	public function getConstraint()
373
+	{
374
+		return $this->constraint;
375
+	}
376
+
377
+	/**
378
+	 * Performs a logical conjunction of the given constraints. The method takes one or more contraints and concatenates them with a boolean AND.
379
+	 * It also scepts a single array of constraints to be concatenated.
380
+	 *
381
+	 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
382
+	 * @throws InvalidNumberOfConstraintsException
383
+	 * @return AndInterface
384
+	 * @api
385
+	 */
386
+	public function logicalAnd($constraint1)
387
+	{
388
+		if (is_array($constraint1)) {
389
+			$resultingConstraint = array_shift($constraint1);
390
+			$constraints = $constraint1;
391
+		} else {
392
+			$constraints = func_get_args();
393
+			$resultingConstraint = array_shift($constraints);
394
+		}
395
+		if ($resultingConstraint === null) {
396
+			throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289500);
397
+		}
398
+		foreach ($constraints as $constraint) {
399
+			$resultingConstraint = $this->qomFactory->_and($resultingConstraint, $constraint);
400
+		}
401
+		return $resultingConstraint;
402
+	}
403
+
404
+	/**
405
+	 * Performs a logical disjunction of the two given constraints
406
+	 *
407
+	 * @param mixed $constraint1 The first of multiple constraints or an array of constraints.
408
+	 * @throws InvalidNumberOfConstraintsException
409
+	 * @return OrInterface
410
+	 * @api
411
+	 */
412
+	public function logicalOr($constraint1)
413
+	{
414
+		if (is_array($constraint1)) {
415
+			$resultingConstraint = array_shift($constraint1);
416
+			$constraints = $constraint1;
417
+		} else {
418
+			$constraints = func_get_args();
419
+			$resultingConstraint = array_shift($constraints);
420
+		}
421
+		if ($resultingConstraint === null) {
422
+			throw new InvalidNumberOfConstraintsException('There must be at least one constraint or a non-empty array of constraints given.', 1401289501);
423
+		}
424
+		foreach ($constraints as $constraint) {
425
+			$resultingConstraint = $this->qomFactory->_or($resultingConstraint, $constraint);
426
+		}
427
+		return $resultingConstraint;
428
+	}
429
+
430
+	/**
431
+	 * Performs a logical negation of the given constraint
432
+	 *
433
+	 * @param ConstraintInterface $constraint Constraint to negate
434
+	 * @throws \RuntimeException
435
+	 * @return NotInterface
436
+	 * @api
437
+	 */
438
+	public function logicalNot(ConstraintInterface $constraint)
439
+	{
440
+		return $this->qomFactory->not($constraint);
441
+	}
442
+
443
+	/**
444
+	 * Returns an equals criterion used for matching objects against a query
445
+	 *
446
+	 * @param string $propertyName The name of the property to compare against
447
+	 * @param mixed $operand The value to compare with
448
+	 * @param boolean $caseSensitive Whether the equality test should be done case-sensitive
449
+	 * @return ComparisonInterface
450
+	 * @api
451
+	 */
452
+	public function equals($propertyName, $operand, $caseSensitive = true)
453
+	{
454
+		if (is_object($operand) || $caseSensitive) {
455
+			$comparison = $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_EQUAL_TO, $operand);
456
+		} else {
457
+			$comparison = $this->qomFactory->comparison($this->qomFactory->lowerCase($this->qomFactory->propertyValue($propertyName, $this->getSelectorName())), QueryInterface::OPERATOR_EQUAL_TO, mb_strtolower($operand, \TYPO3\CMS\Extbase\Persistence\Generic\Query::CHARSET));
458
+		}
459
+		return $comparison;
460
+	}
461
+
462
+	/**
463
+	 * Returns a like criterion used for matching objects against a query
464
+	 *
465
+	 * @param string $propertyName The name of the property to compare against
466
+	 * @param mixed $operand The value to compare with
467
+	 * @param boolean $caseSensitive Whether the matching should be done case-sensitive
468
+	 * @return ComparisonInterface
469
+	 * @api
470
+	 */
471
+	public function like($propertyName, $operand, $caseSensitive = true)
472
+	{
473
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LIKE, $operand);
474
+	}
475
+
476
+	/**
477
+	 * Returns a "contains" criterion used for matching objects against a query.
478
+	 * It matches if the multivalued property contains the given operand.
479
+	 *
480
+	 * @param string $propertyName The name of the (multivalued) property to compare against
481
+	 * @param mixed $operand The value to compare with
482
+	 * @return ComparisonInterface
483
+	 * @api
484
+	 */
485
+	public function contains($propertyName, $operand)
486
+	{
487
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_CONTAINS, $operand);
488
+	}
489
+
490
+	/**
491
+	 * Returns an "in" criterion used for matching objects against a query. It
492
+	 * matches if the property's value is contained in the multivalued operand.
493
+	 *
494
+	 * @param string $propertyName The name of the property to compare against
495
+	 * @param mixed $operand The value to compare with, multivalued
496
+	 * @throws UnexpectedTypeException
497
+	 * @return ComparisonInterface
498
+	 * @api
499
+	 */
500
+	public function in($propertyName, $operand)
501
+	{
502
+		if (!is_array($operand) && !$operand instanceof \ArrayAccess && !$operand instanceof \Traversable) {
503
+			throw new UnexpectedTypeException('The "in" operator must be given a mutlivalued operand (array, ArrayAccess, Traversable).', 1264678095);
504
+		}
505
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_IN, $operand);
506
+	}
507
+
508
+	/**
509
+	 * Returns a less than criterion used for matching objects against a query
510
+	 *
511
+	 * @param string $propertyName The name of the property to compare against
512
+	 * @param mixed $operand The value to compare with
513
+	 * @return ComparisonInterface
514
+	 * @api
515
+	 */
516
+	public function lessThan($propertyName, $operand)
517
+	{
518
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN, $operand);
519
+	}
520
+
521
+	/**
522
+	 * Returns a less or equal than criterion used for matching objects against a query
523
+	 *
524
+	 * @param string $propertyName The name of the property to compare against
525
+	 * @param mixed $operand The value to compare with
526
+	 * @return ComparisonInterface
527
+	 * @api
528
+	 */
529
+	public function lessThanOrEqual($propertyName, $operand)
530
+	{
531
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO, $operand);
532
+	}
533
+
534
+	/**
535
+	 * Returns a greater than criterion used for matching objects against a query
536
+	 *
537
+	 * @param string $propertyName The name of the property to compare against
538
+	 * @param mixed $operand The value to compare with
539
+	 * @return ComparisonInterface
540
+	 * @api
541
+	 */
542
+	public function greaterThan($propertyName, $operand)
543
+	{
544
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN, $operand);
545
+	}
546
+
547
+	/**
548
+	 * Returns a greater than or equal criterion used for matching objects against a query
549
+	 *
550
+	 * @param string $propertyName The name of the property to compare against
551
+	 * @param mixed $operand The value to compare with
552
+	 * @return ComparisonInterface
553
+	 * @api
554
+	 */
555
+	public function greaterThanOrEqual($propertyName, $operand)
556
+	{
557
+		return $this->qomFactory->comparison($this->qomFactory->propertyValue($propertyName, $this->getSelectorName()), QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO, $operand);
558
+	}
559
+
560
+	/**
561
+	 * Returns the query result count.
562
+	 *
563
+	 * @return integer The query result count
564
+	 * @api
565
+	 */
566
+	public function count()
567
+	{
568
+		/** @var VidiDbBackend $backend */
569
+		$backend = GeneralUtility::makeInstance(VidiDbBackend::class, $this);
570
+		return $backend->countResult();
571
+	}
572
+
573
+	/**
574
+	 * Returns an "isEmpty" criterion used for matching objects against a query.
575
+	 * It matches if the multivalued property contains no values or is null.
576
+	 *
577
+	 * @param string $propertyName The name of the multivalued property to compare against
578
+	 * @throws NotImplementedException
579
+	 * @throws InvalidQueryException if used on a single-valued property
580
+	 * @api
581
+	 */
582
+	public function isEmpty($propertyName)
583
+	{
584
+		throw new NotImplementedException(__METHOD__);
585
+	}
586
+
587
+	/**
588
+	 * @return string
589
+	 */
590
+	public function getDistinct()
591
+	{
592
+		return $this->distinct;
593
+	}
594
+
595
+	/**
596
+	 * @param string $distinct
597
+	 * @return $this
598
+	 */
599
+	public function setDistinct($distinct)
600
+	{
601
+		$this->distinct = $distinct;
602
+		return $this;
603
+	}
604
+
605
+	/**
606
+	 * Sets the statement of this query. If you use this, you will lose the abstraction from a concrete storage
607
+	 * backend (database).
608
+	 *
609
+	 * @param string|\TYPO3\CMS\Core\Database\PreparedStatement $statement The statement
610
+	 * @param array $parameters An array of parameters. These will be bound to placeholders '?' in the $statement.
611
+	 * @return QueryInterface
612
+	 */
613
+	public function statement($statement, array $parameters = array())
614
+	{
615
+		$this->statement = $this->qomFactory->statement($statement, $parameters);
616
+		return $this;
617
+	}
618
+
619
+	/**
620
+	 * Returns the statement of this query.
621
+	 *
622
+	 * @return Statement
623
+	 */
624
+	public function getStatement()
625
+	{
626
+		return $this->statement;
627
+	}
628
+
629
+	/**
630
+	 * Returns whether the current mode is Backend.
631
+	 *
632
+	 * @return bool
633
+	 */
634
+	protected function isBackendMode()
635
+	{
636
+		return ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend();
637
+	}
638
+
639
+	/**
640
+	 * @return string
641
+	 */
642
+	public function getSourceFieldName()
643
+	{
644
+		return $this->sourceFieldName;
645
+	}
646
+
647
+	/**
648
+	 * @param string $sourceFieldName
649
+	 * @return $this
650
+	 */
651
+	public function setSourceFieldName($sourceFieldName)
652
+	{
653
+		$this->sourceFieldName = $sourceFieldName;
654
+		return $this;
655
+	}
656
+
657
+	public function setQuerySettings(QuerySettingsInterface $querySettings)
658
+	{
659
+		$this->typo3QuerySettings = $querySettings;
660
+	}
661
+
662
+	public function getQuerySettings()
663
+	{
664
+		return $this->typo3QuerySettings;
665
+	}
666 666
 }
Please login to merge, or discard this patch.
Classes/Persistence/Storage/VidiDbBackend.php 1 patch
Indentation   +1040 added lines, -1040 removed lines patch added patch discarded remove patch
@@ -51,1044 +51,1044 @@
 block discarded – undo
51 51
  */
52 52
 class VidiDbBackend
53 53
 {
54
-    public const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
55
-    public const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
56
-
57
-    /**
58
-     * The TYPO3 page repository. Used for language and workspace overlay
59
-     *
60
-     * @var PageRepository
61
-     */
62
-    protected $pageRepository;
63
-
64
-    /**
65
-     * @var Query
66
-     */
67
-    protected $query;
68
-
69
-    /**
70
-     * Store some info related to table name and its aliases.
71
-     *
72
-     * @var array
73
-     */
74
-    protected $tableNameAliases = array(
75
-        'aliases' => [],
76
-        'aliasIncrement' => [],
77
-    );
78
-
79
-    /**
80
-     * Use to store the current foreign table name alias.
81
-     *
82
-     * @var string
83
-     */
84
-    protected $currentChildTableNameAlias = '';
85
-
86
-    /**
87
-     * @param Query $query
88
-     */
89
-    public function __construct(Query $query)
90
-    {
91
-        $this->query = $query;
92
-    }
93
-
94
-    /**
95
-     * @param $parameters
96
-     * @return array
97
-     */
98
-    protected static function getTypes($parameters)
99
-    {
100
-        $types = [];
101
-        foreach ($parameters as $parameter) {
102
-            if (is_array($parameter)) {
103
-                if (MathUtility::canBeInterpretedAsInteger($parameter[0])) {
104
-                    $types[] = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
105
-                } else {
106
-                    $types[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
107
-                }
108
-            } else {
109
-                if (MathUtility::canBeInterpretedAsInteger($parameter)) {
110
-                    $types[] = ParameterType::INTEGER;
111
-                } else {
112
-                    $types[] = ParameterType::STRING;
113
-                }
114
-            }
115
-        }
116
-        return $types;
117
-    }
118
-
119
-    /**
120
-     * Returns the result of the query
121
-     */
122
-    public function fetchResult()
123
-    {
124
-        $parameters = [];
125
-        $statementParts = $this->parseQuery($parameters);
126
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
127
-        $sql = $this->buildQuery($statementParts);
128
-        //print $sql; exit();
129
-
130
-        $rows = $this->getConnection()
131
-            ->executeQuery($sql, $parameters, self::getTypes($parameters))
132
-            ->fetchAllAssociative();
133
-
134
-        return $this->getContentObjects($rows);
135
-    }
136
-
137
-    /**
138
-     * Returns the number of tuples matching the query.
139
-     *
140
-     * @return int The number of matching tuples
141
-     */
142
-    public function countResult()
143
-    {
144
-        $parameters = [];
145
-        $statementParts = $this->parseQuery($parameters);
146
-        $statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
147
-        $types = self::getTypes($parameters);
148
-
149
-        // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
150
-        if (!empty($statementParts['limit'])) {
151
-            $sql = $this->buildQuery($statementParts);
152
-
153
-            $count = $this
154
-                ->getConnection()
155
-                ->executeQuery($sql, $parameters, $types)
156
-                ->rowCount();
157
-        } else {
158
-            $statementParts['fields'] = array('COUNT(*)');
159
-            // having orderings without grouping is not compatible with non-MySQL DBMS
160
-            $statementParts['orderings'] = [];
161
-            if (isset($statementParts['keywords']['distinct'])) {
162
-                unset($statementParts['keywords']['distinct']);
163
-                $distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
164
-                $statementParts['fields'] = array('COUNT(DISTINCT ' . $statementParts['mainTable'] . '.' . $distinctField . ')');
165
-            }
166
-
167
-            $sql = $this->buildQuery($statementParts);
168
-            $count = $this
169
-                ->getConnection()
170
-                ->executeQuery($sql, $parameters, $types)
171
-                ->fetchColumn(0);
172
-        }
173
-        return (int)$count;
174
-    }
175
-
176
-    /**
177
-     * Parses the query and returns the SQL statement parts.
178
-     *
179
-     * @param array &$parameters
180
-     * @return array
181
-     */
182
-    public function parseQuery(array &$parameters)
183
-    {
184
-        $statementParts = [];
185
-        $statementParts['keywords'] = [];
186
-        $statementParts['tables'] = [];
187
-        $statementParts['unions'] = [];
188
-        $statementParts['fields'] = [];
189
-        $statementParts['where'] = [];
190
-        $statementParts['additionalWhereClause'] = [];
191
-        $statementParts['orderings'] = [];
192
-        $statementParts['limit'] = [];
193
-        $query = $this->query;
194
-        $source = $query->getSource();
195
-        $this->parseSource($source, $statementParts);
196
-        $this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
197
-        $this->parseOrderings($query->getOrderings(), $source, $statementParts);
198
-        $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
199
-        $tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
200
-        foreach ($tableNames as $tableNameOrAlias) {
201
-            if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
202
-                $this->addAdditionalWhereClause($query->getTypo3QuerySettings(), $tableNameOrAlias, $statementParts);
203
-            }
204
-        }
205
-
206
-        return $statementParts;
207
-    }
208
-
209
-    /**
210
-     * Fiddle with the statement structure to handle recursive MM relations.
211
-     * For the recursive MM query to work, we must invert some values.
212
-     * Let see if that is the best way of doing that...
213
-     *
214
-     * @param array $statementParts
215
-     * @return array
216
-     */
217
-    public function processStatementStructureForRecursiveMMRelation(array $statementParts)
218
-    {
219
-        if ($this->hasRecursiveMMRelation()) {
220
-            $tableName = $this->query->getType();
221
-
222
-            // In order the MM query to work for a recursive MM query, we must invert some values.
223
-            // tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
224
-            $values = [];
225
-            foreach ($statementParts['fields'] as $key => $value) {
226
-                $values[$key] = str_replace($tableName, $tableName . '0', $value);
227
-            }
228
-            $statementParts['fields'] = $values;
229
-
230
-            // Same comment as above.
231
-            $values = [];
232
-            foreach ($statementParts['where'] as $key => $value) {
233
-                $values[$key] = str_replace($tableName . '0', $tableName, $value);
234
-            }
235
-            $statementParts['where'] = $values;
236
-
237
-            // We must be more restrictive by transforming the "left" union by "inner"
238
-            $values = [];
239
-            foreach ($statementParts['unions'] as $key => $value) {
240
-                $values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
241
-            }
242
-            $statementParts['unions'] = $values;
243
-        }
244
-
245
-        return $statementParts;
246
-    }
247
-
248
-    /**
249
-     * Tell whether there is a recursive MM relation.
250
-     *
251
-     * @return bool
252
-     */
253
-    public function hasRecursiveMMRelation()
254
-    {
255
-        return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
256
-    }
257
-
258
-    /**
259
-     * Returns the statement, ready to be executed.
260
-     *
261
-     * @param array $statementParts The SQL statement parts
262
-     * @return string The SQL statement
263
-     */
264
-    public function buildQuery(array $statementParts)
265
-    {
266
-        // Add more statement to the UNION part.
267
-        if (!empty($statementParts['unions'])) {
268
-            foreach ($statementParts['unions'] as $tableName => $unionPart) {
269
-                if (!empty($statementParts['additionalWhereClause'][$tableName])) {
270
-                    $statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
271
-                }
272
-            }
273
-        }
274
-
275
-        $statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
276
-        if (!empty($statementParts['where'])) {
277
-            $statement .= ' WHERE ' . implode('', $statementParts['where']);
278
-            if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
279
-                $statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
280
-            }
281
-        } elseif (!empty($statementParts['additionalWhereClause'])) {
282
-            $statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
283
-        }
284
-        if (!empty($statementParts['orderings'])) {
285
-            $statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
286
-        }
287
-        if (!empty($statementParts['limit'])) {
288
-            $statement .= ' LIMIT ' . $statementParts['limit'];
289
-        }
290
-
291
-        return $statement;
292
-    }
293
-
294
-    /**
295
-     * Transforms a Query Source into SQL and parameter arrays
296
-     *
297
-     * @param SourceInterface $source The source
298
-     * @param array &$sql
299
-     * @return void
300
-     */
301
-    protected function parseSource(SourceInterface $source, array &$sql)
302
-    {
303
-        $tableName = $this->getTableName();
304
-        $sql['fields'][$tableName] = $tableName . '.*';
305
-        if ($this->query->getDistinct()) {
306
-            $sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
307
-            $sql['keywords']['distinct'] = 'DISTINCT';
308
-        }
309
-        $sql['tables'][$tableName] = $tableName;
310
-        $sql['mainTable'] = $tableName;
311
-    }
312
-
313
-    /**
314
-     * Transforms a constraint into SQL and parameter arrays
315
-     *
316
-     * @param ConstraintInterface $constraint The constraint
317
-     * @param SourceInterface $source The source
318
-     * @param array &$statementParts The query parts
319
-     * @param array &$parameters The parameters that will replace the markers
320
-     * @return void
321
-     */
322
-    protected function parseConstraint(ConstraintInterface $constraint = null, SourceInterface $source, array &$statementParts, array &$parameters)
323
-    {
324
-        if ($constraint instanceof AndInterface) {
325
-            $statementParts['where'][] = '(';
326
-            $this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
327
-            $statementParts['where'][] = ' AND ';
328
-            $this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
329
-            $statementParts['where'][] = ')';
330
-        } elseif ($constraint instanceof OrInterface) {
331
-            $statementParts['where'][] = '(';
332
-            $this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
333
-            $statementParts['where'][] = ' OR ';
334
-            $this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
335
-            $statementParts['where'][] = ')';
336
-        } elseif ($constraint instanceof NotInterface) {
337
-            $statementParts['where'][] = 'NOT (';
338
-            $this->parseConstraint($constraint->getConstraint(), $source, $statementParts, $parameters);
339
-            $statementParts['where'][] = ')';
340
-        } elseif ($constraint instanceof ComparisonInterface) {
341
-            $this->parseComparison($constraint, $source, $statementParts, $parameters);
342
-        }
343
-    }
344
-
345
-    /**
346
-     * Parse a Comparison into SQL and parameter arrays.
347
-     *
348
-     * @param ComparisonInterface $comparison The comparison to parse
349
-     * @param SourceInterface $source The source
350
-     * @param array &$statementParts SQL query parts to add to
351
-     * @param array &$parameters Parameters to bind to the SQL
352
-     * @return void
353
-     * @throws Exception\RepositoryException
354
-     */
355
-    protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$statementParts, array &$parameters)
356
-    {
357
-        $operand1 = $comparison->getOperand1();
358
-        $operator = $comparison->getOperator();
359
-        $operand2 = $comparison->getOperand2();
360
-        if ($operator === QueryInterface::OPERATOR_IN) {
361
-            $items = [];
362
-            $hasValue = false;
363
-            foreach ($operand2 as $value) {
364
-                $value = $this->getPlainValue($value);
365
-                if ($value !== null) {
366
-                    $items[] = $value;
367
-                    $hasValue = true;
368
-                }
369
-            }
370
-            if ($hasValue === false) {
371
-                $statementParts['where'][] = '1<>1';
372
-            } else {
373
-                $this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters, null);
374
-                $parameters[] = $items;
375
-            }
376
-        } elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
377
-            if ($operand2 === null) {
378
-                $statementParts['where'][] = '1<>1';
379
-            } else {
380
-                throw new \Exception('Not implemented! Contact extension author.', 1412931227);
381
-                # @todo re-implement me if necessary.
382
-                #$tableName = $this->query->getType();
383
-                #$propertyName = $operand1->getPropertyName();
384
-                #while (strpos($propertyName, '.') !== false) {
385
-                #	$this->addUnionStatement($tableName, $propertyName, $statementParts);
386
-                #}
387
-                #$columnName = $propertyName;
388
-                #$columnMap = $propertyName;
389
-                #$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
390
-                #if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
391
-                #	$relationTableName = $columnMap->getRelationTableName();
392
-                #	$statementParts['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
393
-                #	$parameters[] = intval($this->getPlainValue($operand2));
394
-                #} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
395
-                #	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
396
-                #	if (isset($parentKeyFieldName)) {
397
-                #		$childTableName = $columnMap->getChildTableName();
398
-                #		$statementParts['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
399
-                #		$parameters[] = intval($this->getPlainValue($operand2));
400
-                #	} else {
401
-                #		$statementParts['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
402
-                #		$parameters[] = intval($this->getPlainValue($operand2));
403
-                #	}
404
-                #} else {
405
-                #	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
406
-                #}
407
-            }
408
-        } else {
409
-            if ($operand2 === null) {
410
-                if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
411
-                    $operator = self::OPERATOR_EQUAL_TO_NULL;
412
-                } elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
413
-                    $operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
414
-                }
415
-            }
416
-            $this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters);
417
-            $parameters[] = $this->getPlainValue($operand2);
418
-        }
419
-    }
420
-
421
-    /**
422
-     * Returns a plain value, i.e. objects are flattened if possible.
423
-     *
424
-     * @param mixed $input
425
-     * @return mixed
426
-     * @throws UnexpectedTypeException
427
-     */
428
-    protected function getPlainValue($input)
429
-    {
430
-        if (is_array($input)) {
431
-            throw new UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
432
-        }
433
-        if ($input instanceof \DateTime) {
434
-            return $input->format('U');
435
-        } elseif (is_object($input)) {
436
-            if ($input instanceof LazyLoadingProxy) {
437
-                $realInput = $input->_loadRealInstance();
438
-            } else {
439
-                $realInput = $input;
440
-            }
441
-            if ($realInput instanceof DomainObjectInterface) {
442
-                return $realInput->getUid();
443
-            } else {
444
-                throw new UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
445
-            }
446
-        } elseif (is_bool($input)) {
447
-            return $input === true ? 1 : 0;
448
-        } else {
449
-            return $input;
450
-        }
451
-    }
452
-
453
-    /**
454
-     * Parse a DynamicOperand into SQL and parameter arrays.
455
-     *
456
-     * @param DynamicOperandInterface $operand
457
-     * @param string $operator One of the JCR_OPERATOR_* constants
458
-     * @param SourceInterface $source The source
459
-     * @param array &$statementParts The query parts
460
-     * @param array &$parameters The parameters that will replace the markers
461
-     * @param string $valueFunction an optional SQL function to apply to the operand value
462
-     * @return void
463
-     */
464
-    protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$statementParts, array &$parameters, $valueFunction = null)
465
-    {
466
-        if ($operand instanceof LowerCaseInterface) {
467
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'LOWER');
468
-        } elseif ($operand instanceof UpperCaseInterface) {
469
-            $this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'UPPER');
470
-        } elseif ($operand instanceof PropertyValueInterface) {
471
-            $propertyName = $operand->getPropertyName();
472
-
473
-            // Reset value.
474
-            $this->currentChildTableNameAlias = '';
475
-
476
-            if ($source instanceof SelectorInterface) {
477
-                $tableName = $this->query->getType();
478
-                while (strpos($propertyName, '.') !== false) {
479
-                    $this->addUnionStatement($tableName, $propertyName, $statementParts);
480
-                }
481
-            } elseif ($source instanceof JoinInterface) {
482
-                $tableName = $source->getJoinCondition()->getSelector1Name();
483
-            }
484
-
485
-            $columnName = $propertyName;
486
-            $resolvedOperator = $this->resolveOperator($operator);
487
-            $constraintSQL = '';
488
-
489
-            $marker = $operator === QueryInterface::OPERATOR_IN
490
-                ? '(?)'
491
-                : '?';
492
-
493
-            if ($valueFunction === null) {
494
-                $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $resolvedOperator . ' ' . $marker;
495
-            } else {
496
-                $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $resolvedOperator . ' ' . $marker;
497
-            }
498
-
499
-            if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
500
-                $constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
501
-            }
502
-            $statementParts['where'][] = $constraintSQL;
503
-        }
504
-    }
505
-
506
-    /**
507
-     * @param string &$tableName
508
-     * @param string &$propertyPath
509
-     * @param array &$statementParts
510
-     */
511
-    protected function addUnionStatement(&$tableName, &$propertyPath, array &$statementParts)
512
-    {
513
-        $table = Tca::table($tableName);
514
-
515
-        $explodedPropertyPath = explode('.', $propertyPath, 2);
516
-        $fieldName = $explodedPropertyPath[0];
517
-
518
-        // Field of type "group" are special because property path must contain the table name
519
-        // to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
520
-        $parts = explode('.', $propertyPath, 3);
521
-        if ($table->field($fieldName)->isGroup() && count($parts) > 2) {
522
-            $explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
523
-            $explodedPropertyPath[1] = $parts[2];
524
-            $fieldName = $explodedPropertyPath[0];
525
-        }
526
-
527
-        $parentKeyFieldName = $table->field($fieldName)->getForeignField();
528
-        $childTableName = $table->field($fieldName)->getForeignTable();
529
-
530
-        if ($childTableName === null) {
531
-            throw new InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
532
-        }
533
-
534
-        if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
535
-            // sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
536
-            // $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
537
-            if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
538
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
539
-            } else {
540
-                $statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
541
-            }
542
-        } elseif ($table->field($fieldName)->hasRelationManyToMany()) {
543
-            $relationTableName = $table->field($fieldName)->getManyToManyTable();
544
-
545
-            $parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
546
-            $childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
547
-
548
-            // MM table e.g sys_category_record_mm
549
-            $relationTableNameAlias = $this->generateAlias($relationTableName);
550
-            $join = sprintf(
551
-                'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
552
-                $relationTableName,
553
-                $relationTableNameAlias,
554
-                $tableName,
555
-                $relationTableNameAlias,
556
-                $parentKeyFieldName
557
-            );
558
-            $statementParts['unions'][$relationTableNameAlias] = $join;
559
-
560
-            // Foreign table e.g sys_category
561
-            $childTableNameAlias = $this->generateAlias($childTableName);
562
-            $this->currentChildTableNameAlias = $childTableNameAlias;
563
-            $join = sprintf(
564
-                'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
565
-                $childTableName,
566
-                $childTableNameAlias,
567
-                $relationTableNameAlias,
568
-                $childKeyFieldName,
569
-                $childTableNameAlias
570
-            );
571
-            $statementParts['unions'][$childTableNameAlias] = $join;
572
-
573
-            // Find a possible table name for a MM condition.
574
-            $tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
575
-            if ($tableNameCondition) {
576
-                // If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
577
-                $sourceFileName = $this->query->getSourceFieldName();
578
-                if (empty($sourceFileName)) {
579
-                    $additionalMMConditions = array(
580
-                        'tablenames' => $tableNameCondition,
581
-                    );
582
-                } else {
583
-                    $additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
584
-                }
585
-
586
-                foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
587
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
588
-                    $statementParts['unions'][$relationTableNameAlias] .= $additionalJoin;
589
-
590
-                    $additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
591
-                    $statementParts['unions'][$childTableNameAlias] .= $additionalJoin;
592
-                }
593
-            }
594
-        } elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
595
-            $childTableNameAlias = $this->generateAlias($childTableName);
596
-            $this->currentChildTableNameAlias = $childTableNameAlias;
597
-
598
-            if (isset($parentKeyFieldName)) {
599
-                $join = sprintf(
600
-                    'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
601
-                    $childTableName,
602
-                    $childTableNameAlias,
603
-                    $tableName,
604
-                    $childTableNameAlias,
605
-                    $parentKeyFieldName
606
-                );
607
-                $statementParts['unions'][$childTableNameAlias] = $join;
608
-            } else {
609
-                $join = sprintf(
610
-                    'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
611
-                    $childTableName,
612
-                    $childTableNameAlias,
613
-                    $childTableNameAlias,
614
-                    $tableName,
615
-                    $fieldName
616
-                );
617
-                $statementParts['unions'][$childTableNameAlias] = $join;
618
-            }
619
-        } else {
620
-            throw new Exception('Could not determine type of relation.', 1252502725);
621
-        }
622
-
623
-        $statementParts['keywords']['distinct'] = 'DISTINCT';
624
-        $propertyPath = $explodedPropertyPath[1];
625
-        $tableName = $childTableName;
626
-    }
627
-
628
-    /**
629
-     * Returns the SQL operator for the given JCR operator type.
630
-     *
631
-     * @param string $operator One of the JCR_OPERATOR_* constants
632
-     * @return string an SQL operator
633
-     * @throws Exception
634
-     */
635
-    protected function resolveOperator($operator)
636
-    {
637
-        switch ($operator) {
638
-            case self::OPERATOR_EQUAL_TO_NULL:
639
-                $operator = 'IS';
640
-                break;
641
-            case self::OPERATOR_NOT_EQUAL_TO_NULL:
642
-                $operator = 'IS NOT';
643
-                break;
644
-            case QueryInterface::OPERATOR_IN:
645
-                $operator = 'IN';
646
-                break;
647
-            case QueryInterface::OPERATOR_EQUAL_TO:
648
-                $operator = '=';
649
-                break;
650
-            case QueryInterface::OPERATOR_NOT_EQUAL_TO:
651
-                $operator = '!=';
652
-                break;
653
-            case QueryInterface::OPERATOR_LESS_THAN:
654
-                $operator = '<';
655
-                break;
656
-            case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
657
-                $operator = '<=';
658
-                break;
659
-            case QueryInterface::OPERATOR_GREATER_THAN:
660
-                $operator = '>';
661
-                break;
662
-            case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
663
-                $operator = '>=';
664
-                break;
665
-            case QueryInterface::OPERATOR_LIKE:
666
-                $operator = 'LIKE';
667
-                break;
668
-            default:
669
-                throw new Exception('Unsupported operator encountered.', 1242816073);
670
-        }
671
-        return $operator;
672
-    }
673
-
674
-    /**
675
-     * Adds additional WHERE statements according to the query settings.
676
-     *
677
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
678
-     * @param string $tableNameOrAlias The table name to add the additional where clause for
679
-     * @param array &$statementParts
680
-     * @return void
681
-     */
682
-    protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
683
-    {
684
-        $this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
685
-        if ($querySettings->getRespectSysLanguage()) {
686
-            $this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
687
-        }
688
-    }
689
-
690
-    /**
691
-     * Adds enableFields and deletedClause to the query if necessary
692
-     *
693
-     * @param QuerySettingsInterface $querySettings
694
-     * @param string $tableNameOrAlias The database table name
695
-     * @param array &$statementParts The query parts
696
-     * @return void
697
-     */
698
-    protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
699
-    {
700
-        $statement = '';
701
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
702
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
703
-            $ignoreEnableFields = $querySettings->getIgnoreEnableFields();
704
-            $enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
705
-            $includeDeleted = $querySettings->getIncludeDeleted();
706
-            if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()) {
707
-                $statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
708
-            } else {
709
-                // 'BE' case
710
-                $statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
711
-            }
712
-
713
-            // Remove the prefixing "AND" if any.
714
-            if (!empty($statement)) {
715
-                $statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
716
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
717
-            }
718
-        }
719
-    }
720
-
721
-    /**
722
-     * Returns constraint statement for frontend context
723
-     *
724
-     * @param string $tableNameOrAlias
725
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
726
-     * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is null or an empty array (default) all enable fields are ignored.
727
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
728
-     * @return string
729
-     * @throws Exception\InconsistentQuerySettingsException
730
-     */
731
-    protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted)
732
-    {
733
-        $statement = '';
734
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
735
-        if ($ignoreEnableFields && !$includeDeleted) {
736
-            if (count($enableFieldsToBeIgnored)) {
737
-                // array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
738
-                $statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
739
-            } else {
740
-                $statement .= $this->getPageRepository()->deleteClause($tableName);
741
-            }
742
-        } elseif (!$ignoreEnableFields && !$includeDeleted) {
743
-            $statement .= $this->getPageRepository()->enableFields($tableName);
744
-        } elseif (!$ignoreEnableFields && $includeDeleted) {
745
-            throw new InconsistentQuerySettingsException('Query setting "ignoreEnableFields=false" can not be used together with "includeDeleted=true" in frontend context.', 1327678173);
746
-        }
747
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
748
-    }
749
-
750
-    /**
751
-     * Returns constraint statement for backend context
752
-     *
753
-     * @param string $tableNameOrAlias
754
-     * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
755
-     * @param boolean $includeDeleted A flag indicating whether deleted records should be included
756
-     * @return string
757
-     */
758
-    protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
759
-    {
760
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
761
-        $statement = '';
762
-        if (!$ignoreEnableFields) {
763
-            $statement .= BackendUtility::BEenableFields($tableName);
764
-        }
765
-
766
-        // If the table is found to have "workspace" support, add the corresponding fields in the statement.
767
-        if (Tca::table($tableName)->hasWorkspaceSupport()) {
768
-            if ($this->getBackendUser()->workspace === 0) {
769
-                $statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
770
-            } else {
771
-                // Show only records of live and of the current workspace
772
-                // In case we are in a Versioning preview
773
-                $statement .= ' AND (' .
774
-                    $tableName . '.t3ver_wsid=0 OR ' .
775
-                    $tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
776
-                    ')';
777
-            }
778
-
779
-            // Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
780
-            $statement .= ' AND ' . $tableName . '.pid<>-1';
781
-        }
782
-
783
-        if (!$includeDeleted) {
784
-            $statement .= BackendUtility::deleteClause($tableName);
785
-        }
786
-
787
-        return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
788
-    }
789
-
790
-    /**
791
-     * Builds the language field statement
792
-     *
793
-     * @param string $tableNameOrAlias The database table name
794
-     * @param array &$statementParts The query parts
795
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
796
-     * @return void
797
-     * @throws Exception
798
-     */
799
-    protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
800
-    {
801
-        $tableName = $this->resolveTableNameAlias($tableNameOrAlias);
802
-        if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
803
-            if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
804
-                // Select all entries for the current language
805
-                $additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
806
-                // If any language is set -> get those entries which are not translated yet
807
-                // They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
808
-                if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
809
-                    && $querySettings->getLanguageUid() > 0
810
-                ) {
811
-                    $additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
812
-                        ' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
813
-                        ' FROM ' . $tableName .
814
-                        ' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
815
-                        ' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
816
-
817
-                    // Add delete clause to ensure all entries are loaded
818
-                    if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
819
-                        $additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
820
-                    }
821
-                    $additionalWhereClause .= '))';
822
-                }
823
-                $statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
824
-            }
825
-        }
826
-    }
827
-
828
-    /**
829
-     * Transforms orderings into SQL.
830
-     *
831
-     * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
832
-     * @param SourceInterface $source The source
833
-     * @param array &$statementParts The query parts
834
-     * @return void
835
-     * @throws Exception\UnsupportedOrderException
836
-     */
837
-    protected function parseOrderings(array $orderings, SourceInterface $source, array &$statementParts)
838
-    {
839
-        foreach ($orderings as $fieldNameAndPath => $order) {
840
-            switch ($order) {
841
-                case QueryInterface::ORDER_ASCENDING:
842
-                    $order = 'ASC';
843
-                    break;
844
-                case QueryInterface::ORDER_DESCENDING:
845
-                    $order = 'DESC';
846
-                    break;
847
-                default:
848
-                    throw new UnsupportedOrderException('Unsupported order encountered.', 1456845126);
849
-            }
850
-
851
-            $tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
852
-            $fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
853
-            $statementParts['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
854
-        }
855
-    }
856
-
857
-    /**
858
-     * Transforms limit and offset into SQL
859
-     *
860
-     * @param int $limit
861
-     * @param int $offset
862
-     * @param array &$statementParts
863
-     * @return void
864
-     */
865
-    protected function parseLimitAndOffset($limit, $offset, array &$statementParts)
866
-    {
867
-        if ($limit !== null && $offset !== null) {
868
-            $statementParts['limit'] = intval($offset) . ', ' . intval($limit);
869
-        } elseif ($limit !== null) {
870
-            $statementParts['limit'] = intval($limit);
871
-        }
872
-    }
873
-
874
-    /**
875
-     * @param array $rows
876
-     * @return array
877
-     */
878
-    protected function getContentObjects(array $rows): array
879
-    {
880
-        $contentObjects = [];
881
-        foreach ($rows as $row) {
882
-            // Get language uid from querySettings.
883
-            // Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
884
-            $overlaidRow = $this->doLanguageAndWorkspaceOverlay(
885
-                $row,
886
-                $this->query->getTypo3QuerySettings()
887
-            );
888
-
889
-            $contentObjects[] = GeneralUtility::makeInstance(
890
-                Content::class,
891
-                $this->query->getType(),
892
-                $overlaidRow
893
-            );
894
-        }
895
-
896
-        return $contentObjects;
897
-    }
898
-
899
-    /**
900
-     * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
901
-     * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
902
-     *
903
-     * @param array $row
904
-     * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
905
-     * @return array
906
-     */
907
-    protected function doLanguageAndWorkspaceOverlay(array $row, $querySettings)
908
-    {
909
-        $tableName = $this->getTableName();
910
-
911
-        $pageRepository = $this->getPageRepository();
912
-        if (is_object($GLOBALS['TSFE'])) {
913
-            $languageMode = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'legacyLanguageMode');
914
-        #if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
915
-            #    $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
916
-        #}
917
-        } else {
918
-            $languageMode = '';
919
-            $workspaceUid = $this->getBackendUser()->workspace;
920
-            #$pageRepository->versioningWorkspaceId = $workspaceUid;
921
-            #if ($this->getBackendUser()->workspace !== 0) {
922
-            #    $pageRepository->versioningPreview = 1;
923
-            #}
924
-        }
925
-
926
-        // If current row is a translation select its parent
927
-        if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
928
-            && isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
929
-        ) {
930
-            if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
931
-                && $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
932
-            ) {
933
-                $queryBuilder = $this->getQueryBuilder();
934
-                $row = $queryBuilder
935
-                    ->select($tableName . '.*')
936
-                    ->from($tableName)
937
-                    ->andWhere(
938
-                        $tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
939
-                        $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = 0'
940
-                    )
941
-                    ->execute()
942
-                    ->fetch();
943
-            }
944
-        }
945
-
946
-        // Retrieve the original uid; Used for Workspaces!
947
-        if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
948
-            $pageRepository->versionOL($tableName, $row, true, true);
949
-        } else {
950
-            \TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL($tableName, $row);
951
-        }
952
-        if (isset($row['_ORIG_uid'])) {
953
-            $row['uid'] = $row['_ORIG_uid'];
954
-        }
955
-
956
-        // Special case for table "pages"
957
-        if ($tableName == 'pages') {
958
-            $row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
959
-        } elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
960
-            && $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
961
-        ) {
962
-            if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
963
-                $overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
964
-                $row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
965
-            }
966
-        }
967
-
968
-        return $row;
969
-    }
970
-
971
-    /**
972
-     * Return a resolved table name given a possible table name alias.
973
-     *
974
-     * @param string $tableNameOrAlias
975
-     * @return string
976
-     */
977
-    protected function resolveTableNameAlias($tableNameOrAlias)
978
-    {
979
-        $resolvedTableName = $tableNameOrAlias;
980
-        if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
981
-            $resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
982
-        }
983
-        return $resolvedTableName;
984
-    }
985
-
986
-    /**
987
-     * Generate a unique table name alias for the given table name.
988
-     *
989
-     * @param string $tableName
990
-     * @return string
991
-     */
992
-    protected function generateAlias($tableName)
993
-    {
994
-        if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
995
-            $this->tableNameAliases['aliasIncrement'][$tableName] = 0;
996
-        }
997
-
998
-        $numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
999
-        $tableNameAlias = $tableName . $numberOfAliases;
1000
-
1001
-        $this->tableNameAliases['aliasIncrement'][$tableName]++;
1002
-        $this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1003
-
1004
-        return $tableNameAlias;
1005
-    }
1006
-
1007
-    /**
1008
-     * Replace the table names by its table name alias within the given statement.
1009
-     *
1010
-     * @param string $tableName
1011
-     * @param string $tableNameAlias
1012
-     * @param string $statement
1013
-     * @return string
1014
-     */
1015
-    protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1016
-    {
1017
-        if ($statement && $tableName !== $tableNameAlias) {
1018
-            $statement = str_replace($tableName, $tableNameAlias, $statement);
1019
-        }
1020
-        return $statement;
1021
-    }
1022
-
1023
-    /**
1024
-     * Returns an instance of the current Backend User.
1025
-     *
1026
-     * @return BackendUserAuthentication
1027
-     */
1028
-    protected function getBackendUser()
1029
-    {
1030
-        return $GLOBALS['BE_USER'];
1031
-    }
1032
-
1033
-    /**
1034
-     * Tell whether a Backend User is logged in.
1035
-     *
1036
-     * @return bool
1037
-     */
1038
-    protected function isBackendUserLogged()
1039
-    {
1040
-        return is_object($GLOBALS['BE_USER']);
1041
-    }
1042
-
1043
-    /**
1044
-     * @return PageRepository|object
1045
-     */
1046
-    protected function getPageRepository()
1047
-    {
1048
-        if (!$this->pageRepository instanceof PageRepository) {
1049
-            if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() && is_object($GLOBALS['TSFE'])) {
1050
-                $this->pageRepository = $GLOBALS['TSFE']->sys_page;
1051
-            } else {
1052
-                $this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1053
-            }
1054
-        }
1055
-
1056
-        return $this->pageRepository;
1057
-    }
1058
-
1059
-    /**
1060
-     * @return FieldPathResolver|object
1061
-     */
1062
-    protected function getFieldPathResolver()
1063
-    {
1064
-        return GeneralUtility::makeInstance(FieldPathResolver::class);
1065
-    }
1066
-
1067
-    /**
1068
-     * @return object|Connection
1069
-     */
1070
-    protected function getConnection(): Connection
1071
-    {
1072
-        /** @var ConnectionPool $connectionPool */
1073
-        return GeneralUtility::makeInstance(ConnectionPool::class)
1074
-            ->getConnectionForTable($this->getTableName());
1075
-    }
1076
-
1077
-    /**
1078
-     * @return object|QueryBuilder
1079
-     */
1080
-    protected function getQueryBuilder(): QueryBuilder
1081
-    {
1082
-        /** @var ConnectionPool $connectionPool */
1083
-        $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1084
-        return $connectionPool->getQueryBuilderForTable($this->getTableName());
1085
-    }
1086
-
1087
-    /**
1088
-     * @return string
1089
-     */
1090
-    public function getTableName(): string
1091
-    {
1092
-        return $this->query->getSource()->getNodeTypeName(); // getSelectorName()
1093
-    }
54
+	public const OPERATOR_EQUAL_TO_NULL = 'operatorEqualToNull';
55
+	public const OPERATOR_NOT_EQUAL_TO_NULL = 'operatorNotEqualToNull';
56
+
57
+	/**
58
+	 * The TYPO3 page repository. Used for language and workspace overlay
59
+	 *
60
+	 * @var PageRepository
61
+	 */
62
+	protected $pageRepository;
63
+
64
+	/**
65
+	 * @var Query
66
+	 */
67
+	protected $query;
68
+
69
+	/**
70
+	 * Store some info related to table name and its aliases.
71
+	 *
72
+	 * @var array
73
+	 */
74
+	protected $tableNameAliases = array(
75
+		'aliases' => [],
76
+		'aliasIncrement' => [],
77
+	);
78
+
79
+	/**
80
+	 * Use to store the current foreign table name alias.
81
+	 *
82
+	 * @var string
83
+	 */
84
+	protected $currentChildTableNameAlias = '';
85
+
86
+	/**
87
+	 * @param Query $query
88
+	 */
89
+	public function __construct(Query $query)
90
+	{
91
+		$this->query = $query;
92
+	}
93
+
94
+	/**
95
+	 * @param $parameters
96
+	 * @return array
97
+	 */
98
+	protected static function getTypes($parameters)
99
+	{
100
+		$types = [];
101
+		foreach ($parameters as $parameter) {
102
+			if (is_array($parameter)) {
103
+				if (MathUtility::canBeInterpretedAsInteger($parameter[0])) {
104
+					$types[] = \Doctrine\DBAL\Connection::PARAM_INT_ARRAY;
105
+				} else {
106
+					$types[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
107
+				}
108
+			} else {
109
+				if (MathUtility::canBeInterpretedAsInteger($parameter)) {
110
+					$types[] = ParameterType::INTEGER;
111
+				} else {
112
+					$types[] = ParameterType::STRING;
113
+				}
114
+			}
115
+		}
116
+		return $types;
117
+	}
118
+
119
+	/**
120
+	 * Returns the result of the query
121
+	 */
122
+	public function fetchResult()
123
+	{
124
+		$parameters = [];
125
+		$statementParts = $this->parseQuery($parameters);
126
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
127
+		$sql = $this->buildQuery($statementParts);
128
+		//print $sql; exit();
129
+
130
+		$rows = $this->getConnection()
131
+			->executeQuery($sql, $parameters, self::getTypes($parameters))
132
+			->fetchAllAssociative();
133
+
134
+		return $this->getContentObjects($rows);
135
+	}
136
+
137
+	/**
138
+	 * Returns the number of tuples matching the query.
139
+	 *
140
+	 * @return int The number of matching tuples
141
+	 */
142
+	public function countResult()
143
+	{
144
+		$parameters = [];
145
+		$statementParts = $this->parseQuery($parameters);
146
+		$statementParts = $this->processStatementStructureForRecursiveMMRelation($statementParts);
147
+		$types = self::getTypes($parameters);
148
+
149
+		// if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
150
+		if (!empty($statementParts['limit'])) {
151
+			$sql = $this->buildQuery($statementParts);
152
+
153
+			$count = $this
154
+				->getConnection()
155
+				->executeQuery($sql, $parameters, $types)
156
+				->rowCount();
157
+		} else {
158
+			$statementParts['fields'] = array('COUNT(*)');
159
+			// having orderings without grouping is not compatible with non-MySQL DBMS
160
+			$statementParts['orderings'] = [];
161
+			if (isset($statementParts['keywords']['distinct'])) {
162
+				unset($statementParts['keywords']['distinct']);
163
+				$distinctField = $this->query->getDistinct() ? $this->query->getDistinct() : 'uid';
164
+				$statementParts['fields'] = array('COUNT(DISTINCT ' . $statementParts['mainTable'] . '.' . $distinctField . ')');
165
+			}
166
+
167
+			$sql = $this->buildQuery($statementParts);
168
+			$count = $this
169
+				->getConnection()
170
+				->executeQuery($sql, $parameters, $types)
171
+				->fetchColumn(0);
172
+		}
173
+		return (int)$count;
174
+	}
175
+
176
+	/**
177
+	 * Parses the query and returns the SQL statement parts.
178
+	 *
179
+	 * @param array &$parameters
180
+	 * @return array
181
+	 */
182
+	public function parseQuery(array &$parameters)
183
+	{
184
+		$statementParts = [];
185
+		$statementParts['keywords'] = [];
186
+		$statementParts['tables'] = [];
187
+		$statementParts['unions'] = [];
188
+		$statementParts['fields'] = [];
189
+		$statementParts['where'] = [];
190
+		$statementParts['additionalWhereClause'] = [];
191
+		$statementParts['orderings'] = [];
192
+		$statementParts['limit'] = [];
193
+		$query = $this->query;
194
+		$source = $query->getSource();
195
+		$this->parseSource($source, $statementParts);
196
+		$this->parseConstraint($query->getConstraint(), $source, $statementParts, $parameters);
197
+		$this->parseOrderings($query->getOrderings(), $source, $statementParts);
198
+		$this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $statementParts);
199
+		$tableNames = array_unique(array_keys($statementParts['tables'] + $statementParts['unions']));
200
+		foreach ($tableNames as $tableNameOrAlias) {
201
+			if (is_string($tableNameOrAlias) && strlen($tableNameOrAlias) > 0) {
202
+				$this->addAdditionalWhereClause($query->getTypo3QuerySettings(), $tableNameOrAlias, $statementParts);
203
+			}
204
+		}
205
+
206
+		return $statementParts;
207
+	}
208
+
209
+	/**
210
+	 * Fiddle with the statement structure to handle recursive MM relations.
211
+	 * For the recursive MM query to work, we must invert some values.
212
+	 * Let see if that is the best way of doing that...
213
+	 *
214
+	 * @param array $statementParts
215
+	 * @return array
216
+	 */
217
+	public function processStatementStructureForRecursiveMMRelation(array $statementParts)
218
+	{
219
+		if ($this->hasRecursiveMMRelation()) {
220
+			$tableName = $this->query->getType();
221
+
222
+			// In order the MM query to work for a recursive MM query, we must invert some values.
223
+			// tx_domain_model_foo0 (the alias) <--> tx_domain_model_foo (the origin table name)
224
+			$values = [];
225
+			foreach ($statementParts['fields'] as $key => $value) {
226
+				$values[$key] = str_replace($tableName, $tableName . '0', $value);
227
+			}
228
+			$statementParts['fields'] = $values;
229
+
230
+			// Same comment as above.
231
+			$values = [];
232
+			foreach ($statementParts['where'] as $key => $value) {
233
+				$values[$key] = str_replace($tableName . '0', $tableName, $value);
234
+			}
235
+			$statementParts['where'] = $values;
236
+
237
+			// We must be more restrictive by transforming the "left" union by "inner"
238
+			$values = [];
239
+			foreach ($statementParts['unions'] as $key => $value) {
240
+				$values[$key] = str_replace('LEFT JOIN', 'INNER JOIN', $value);
241
+			}
242
+			$statementParts['unions'] = $values;
243
+		}
244
+
245
+		return $statementParts;
246
+	}
247
+
248
+	/**
249
+	 * Tell whether there is a recursive MM relation.
250
+	 *
251
+	 * @return bool
252
+	 */
253
+	public function hasRecursiveMMRelation()
254
+	{
255
+		return isset($this->tableNameAliases['aliasIncrement'][$this->query->getType()]);
256
+	}
257
+
258
+	/**
259
+	 * Returns the statement, ready to be executed.
260
+	 *
261
+	 * @param array $statementParts The SQL statement parts
262
+	 * @return string The SQL statement
263
+	 */
264
+	public function buildQuery(array $statementParts)
265
+	{
266
+		// Add more statement to the UNION part.
267
+		if (!empty($statementParts['unions'])) {
268
+			foreach ($statementParts['unions'] as $tableName => $unionPart) {
269
+				if (!empty($statementParts['additionalWhereClause'][$tableName])) {
270
+					$statementParts['unions'][$tableName] .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$tableName]);
271
+				}
272
+			}
273
+		}
274
+
275
+		$statement = 'SELECT ' . implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']) . ' FROM ' . implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']);
276
+		if (!empty($statementParts['where'])) {
277
+			$statement .= ' WHERE ' . implode('', $statementParts['where']);
278
+			if (!empty($statementParts['additionalWhereClause'][$this->query->getType()])) {
279
+				$statement .= ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
280
+			}
281
+		} elseif (!empty($statementParts['additionalWhereClause'])) {
282
+			$statement .= ' WHERE ' . implode(' AND ', $statementParts['additionalWhereClause'][$this->query->getType()]);
283
+		}
284
+		if (!empty($statementParts['orderings'])) {
285
+			$statement .= ' ORDER BY ' . implode(', ', $statementParts['orderings']);
286
+		}
287
+		if (!empty($statementParts['limit'])) {
288
+			$statement .= ' LIMIT ' . $statementParts['limit'];
289
+		}
290
+
291
+		return $statement;
292
+	}
293
+
294
+	/**
295
+	 * Transforms a Query Source into SQL and parameter arrays
296
+	 *
297
+	 * @param SourceInterface $source The source
298
+	 * @param array &$sql
299
+	 * @return void
300
+	 */
301
+	protected function parseSource(SourceInterface $source, array &$sql)
302
+	{
303
+		$tableName = $this->getTableName();
304
+		$sql['fields'][$tableName] = $tableName . '.*';
305
+		if ($this->query->getDistinct()) {
306
+			$sql['fields'][$tableName] = $tableName . '.' . $this->query->getDistinct();
307
+			$sql['keywords']['distinct'] = 'DISTINCT';
308
+		}
309
+		$sql['tables'][$tableName] = $tableName;
310
+		$sql['mainTable'] = $tableName;
311
+	}
312
+
313
+	/**
314
+	 * Transforms a constraint into SQL and parameter arrays
315
+	 *
316
+	 * @param ConstraintInterface $constraint The constraint
317
+	 * @param SourceInterface $source The source
318
+	 * @param array &$statementParts The query parts
319
+	 * @param array &$parameters The parameters that will replace the markers
320
+	 * @return void
321
+	 */
322
+	protected function parseConstraint(ConstraintInterface $constraint = null, SourceInterface $source, array &$statementParts, array &$parameters)
323
+	{
324
+		if ($constraint instanceof AndInterface) {
325
+			$statementParts['where'][] = '(';
326
+			$this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
327
+			$statementParts['where'][] = ' AND ';
328
+			$this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
329
+			$statementParts['where'][] = ')';
330
+		} elseif ($constraint instanceof OrInterface) {
331
+			$statementParts['where'][] = '(';
332
+			$this->parseConstraint($constraint->getConstraint1(), $source, $statementParts, $parameters);
333
+			$statementParts['where'][] = ' OR ';
334
+			$this->parseConstraint($constraint->getConstraint2(), $source, $statementParts, $parameters);
335
+			$statementParts['where'][] = ')';
336
+		} elseif ($constraint instanceof NotInterface) {
337
+			$statementParts['where'][] = 'NOT (';
338
+			$this->parseConstraint($constraint->getConstraint(), $source, $statementParts, $parameters);
339
+			$statementParts['where'][] = ')';
340
+		} elseif ($constraint instanceof ComparisonInterface) {
341
+			$this->parseComparison($constraint, $source, $statementParts, $parameters);
342
+		}
343
+	}
344
+
345
+	/**
346
+	 * Parse a Comparison into SQL and parameter arrays.
347
+	 *
348
+	 * @param ComparisonInterface $comparison The comparison to parse
349
+	 * @param SourceInterface $source The source
350
+	 * @param array &$statementParts SQL query parts to add to
351
+	 * @param array &$parameters Parameters to bind to the SQL
352
+	 * @return void
353
+	 * @throws Exception\RepositoryException
354
+	 */
355
+	protected function parseComparison(ComparisonInterface $comparison, SourceInterface $source, array &$statementParts, array &$parameters)
356
+	{
357
+		$operand1 = $comparison->getOperand1();
358
+		$operator = $comparison->getOperator();
359
+		$operand2 = $comparison->getOperand2();
360
+		if ($operator === QueryInterface::OPERATOR_IN) {
361
+			$items = [];
362
+			$hasValue = false;
363
+			foreach ($operand2 as $value) {
364
+				$value = $this->getPlainValue($value);
365
+				if ($value !== null) {
366
+					$items[] = $value;
367
+					$hasValue = true;
368
+				}
369
+			}
370
+			if ($hasValue === false) {
371
+				$statementParts['where'][] = '1<>1';
372
+			} else {
373
+				$this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters, null);
374
+				$parameters[] = $items;
375
+			}
376
+		} elseif ($operator === QueryInterface::OPERATOR_CONTAINS) {
377
+			if ($operand2 === null) {
378
+				$statementParts['where'][] = '1<>1';
379
+			} else {
380
+				throw new \Exception('Not implemented! Contact extension author.', 1412931227);
381
+				# @todo re-implement me if necessary.
382
+				#$tableName = $this->query->getType();
383
+				#$propertyName = $operand1->getPropertyName();
384
+				#while (strpos($propertyName, '.') !== false) {
385
+				#	$this->addUnionStatement($tableName, $propertyName, $statementParts);
386
+				#}
387
+				#$columnName = $propertyName;
388
+				#$columnMap = $propertyName;
389
+				#$typeOfRelation = $columnMap instanceof ColumnMap ? $columnMap->getTypeOfRelation() : null;
390
+				#if ($typeOfRelation === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
391
+				#	$relationTableName = $columnMap->getRelationTableName();
392
+				#	$statementParts['where'][] = $tableName . '.uid IN (SELECT ' . $columnMap->getParentKeyFieldName() . ' FROM ' . $relationTableName . ' WHERE ' . $columnMap->getChildKeyFieldName() . '=?)';
393
+				#	$parameters[] = intval($this->getPlainValue($operand2));
394
+				#} elseif ($typeOfRelation === ColumnMap::RELATION_HAS_MANY) {
395
+				#	$parentKeyFieldName = $columnMap->getParentKeyFieldName();
396
+				#	if (isset($parentKeyFieldName)) {
397
+				#		$childTableName = $columnMap->getChildTableName();
398
+				#		$statementParts['where'][] = $tableName . '.uid=(SELECT ' . $childTableName . '.' . $parentKeyFieldName . ' FROM ' . $childTableName . ' WHERE ' . $childTableName . '.uid=?)';
399
+				#		$parameters[] = intval($this->getPlainValue($operand2));
400
+				#	} else {
401
+				#		$statementParts['where'][] = 'FIND_IN_SET(?,' . $tableName . '.' . $columnName . ')';
402
+				#		$parameters[] = intval($this->getPlainValue($operand2));
403
+				#	}
404
+				#} else {
405
+				#	throw new Exception\RepositoryException('Unsupported or non-existing property name "' . $propertyName . '" used in relation matching.', 1327065745);
406
+				#}
407
+			}
408
+		} else {
409
+			if ($operand2 === null) {
410
+				if ($operator === QueryInterface::OPERATOR_EQUAL_TO) {
411
+					$operator = self::OPERATOR_EQUAL_TO_NULL;
412
+				} elseif ($operator === QueryInterface::OPERATOR_NOT_EQUAL_TO) {
413
+					$operator = self::OPERATOR_NOT_EQUAL_TO_NULL;
414
+				}
415
+			}
416
+			$this->parseDynamicOperand($operand1, $operator, $source, $statementParts, $parameters);
417
+			$parameters[] = $this->getPlainValue($operand2);
418
+		}
419
+	}
420
+
421
+	/**
422
+	 * Returns a plain value, i.e. objects are flattened if possible.
423
+	 *
424
+	 * @param mixed $input
425
+	 * @return mixed
426
+	 * @throws UnexpectedTypeException
427
+	 */
428
+	protected function getPlainValue($input)
429
+	{
430
+		if (is_array($input)) {
431
+			throw new UnexpectedTypeException('An array could not be converted to a plain value.', 1274799932);
432
+		}
433
+		if ($input instanceof \DateTime) {
434
+			return $input->format('U');
435
+		} elseif (is_object($input)) {
436
+			if ($input instanceof LazyLoadingProxy) {
437
+				$realInput = $input->_loadRealInstance();
438
+			} else {
439
+				$realInput = $input;
440
+			}
441
+			if ($realInput instanceof DomainObjectInterface) {
442
+				return $realInput->getUid();
443
+			} else {
444
+				throw new UnexpectedTypeException('An object of class "' . get_class($realInput) . '" could not be converted to a plain value.', 1274799934);
445
+			}
446
+		} elseif (is_bool($input)) {
447
+			return $input === true ? 1 : 0;
448
+		} else {
449
+			return $input;
450
+		}
451
+	}
452
+
453
+	/**
454
+	 * Parse a DynamicOperand into SQL and parameter arrays.
455
+	 *
456
+	 * @param DynamicOperandInterface $operand
457
+	 * @param string $operator One of the JCR_OPERATOR_* constants
458
+	 * @param SourceInterface $source The source
459
+	 * @param array &$statementParts The query parts
460
+	 * @param array &$parameters The parameters that will replace the markers
461
+	 * @param string $valueFunction an optional SQL function to apply to the operand value
462
+	 * @return void
463
+	 */
464
+	protected function parseDynamicOperand(DynamicOperandInterface $operand, $operator, SourceInterface $source, array &$statementParts, array &$parameters, $valueFunction = null)
465
+	{
466
+		if ($operand instanceof LowerCaseInterface) {
467
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'LOWER');
468
+		} elseif ($operand instanceof UpperCaseInterface) {
469
+			$this->parseDynamicOperand($operand->getOperand(), $operator, $source, $statementParts, $parameters, 'UPPER');
470
+		} elseif ($operand instanceof PropertyValueInterface) {
471
+			$propertyName = $operand->getPropertyName();
472
+
473
+			// Reset value.
474
+			$this->currentChildTableNameAlias = '';
475
+
476
+			if ($source instanceof SelectorInterface) {
477
+				$tableName = $this->query->getType();
478
+				while (strpos($propertyName, '.') !== false) {
479
+					$this->addUnionStatement($tableName, $propertyName, $statementParts);
480
+				}
481
+			} elseif ($source instanceof JoinInterface) {
482
+				$tableName = $source->getJoinCondition()->getSelector1Name();
483
+			}
484
+
485
+			$columnName = $propertyName;
486
+			$resolvedOperator = $this->resolveOperator($operator);
487
+			$constraintSQL = '';
488
+
489
+			$marker = $operator === QueryInterface::OPERATOR_IN
490
+				? '(?)'
491
+				: '?';
492
+
493
+			if ($valueFunction === null) {
494
+				$constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $resolvedOperator . ' ' . $marker;
495
+			} else {
496
+				$constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $resolvedOperator . ' ' . $marker;
497
+			}
498
+
499
+			if (isset($tableName) && !empty($this->currentChildTableNameAlias)) {
500
+				$constraintSQL = $this->replaceTableNameByAlias($tableName, $this->currentChildTableNameAlias, $constraintSQL);
501
+			}
502
+			$statementParts['where'][] = $constraintSQL;
503
+		}
504
+	}
505
+
506
+	/**
507
+	 * @param string &$tableName
508
+	 * @param string &$propertyPath
509
+	 * @param array &$statementParts
510
+	 */
511
+	protected function addUnionStatement(&$tableName, &$propertyPath, array &$statementParts)
512
+	{
513
+		$table = Tca::table($tableName);
514
+
515
+		$explodedPropertyPath = explode('.', $propertyPath, 2);
516
+		$fieldName = $explodedPropertyPath[0];
517
+
518
+		// Field of type "group" are special because property path must contain the table name
519
+		// to determine the relation type. Example for sys_category, property path will look like "items.sys_file"
520
+		$parts = explode('.', $propertyPath, 3);
521
+		if ($table->field($fieldName)->isGroup() && count($parts) > 2) {
522
+			$explodedPropertyPath[0] = $parts[0] . '.' . $parts[1];
523
+			$explodedPropertyPath[1] = $parts[2];
524
+			$fieldName = $explodedPropertyPath[0];
525
+		}
526
+
527
+		$parentKeyFieldName = $table->field($fieldName)->getForeignField();
528
+		$childTableName = $table->field($fieldName)->getForeignTable();
529
+
530
+		if ($childTableName === null) {
531
+			throw new InvalidRelationConfigurationException('The relation information for property "' . $fieldName . '" of class "' . $tableName . '" is missing.', 1353170925);
532
+		}
533
+
534
+		if ($table->field($fieldName)->hasOne()) { // includes relation "one-to-one" and "many-to-one"
535
+			// sometimes the opposite relation is not defined. We don't want to force this config for backward compatibility reasons.
536
+			// $parentKeyFieldName === null does the trick somehow. Before condition was if (isset($parentKeyFieldName))
537
+			if ($table->field($fieldName)->hasRelationManyToOne() || $parentKeyFieldName === null) {
538
+				$statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.' . $fieldName . '=' . $childTableName . '.uid';
539
+			} else {
540
+				$statementParts['unions'][$childTableName] = 'LEFT JOIN ' . $childTableName . ' ON ' . $tableName . '.uid=' . $childTableName . '.' . $parentKeyFieldName;
541
+			}
542
+		} elseif ($table->field($fieldName)->hasRelationManyToMany()) {
543
+			$relationTableName = $table->field($fieldName)->getManyToManyTable();
544
+
545
+			$parentKeyFieldName = $table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
546
+			$childKeyFieldName = !$table->field($fieldName)->isOppositeRelation() ? 'uid_foreign' : 'uid_local';
547
+
548
+			// MM table e.g sys_category_record_mm
549
+			$relationTableNameAlias = $this->generateAlias($relationTableName);
550
+			$join = sprintf(
551
+				'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
552
+				$relationTableName,
553
+				$relationTableNameAlias,
554
+				$tableName,
555
+				$relationTableNameAlias,
556
+				$parentKeyFieldName
557
+			);
558
+			$statementParts['unions'][$relationTableNameAlias] = $join;
559
+
560
+			// Foreign table e.g sys_category
561
+			$childTableNameAlias = $this->generateAlias($childTableName);
562
+			$this->currentChildTableNameAlias = $childTableNameAlias;
563
+			$join = sprintf(
564
+				'LEFT JOIN %s AS %s ON %s.%s=%s.uid',
565
+				$childTableName,
566
+				$childTableNameAlias,
567
+				$relationTableNameAlias,
568
+				$childKeyFieldName,
569
+				$childTableNameAlias
570
+			);
571
+			$statementParts['unions'][$childTableNameAlias] = $join;
572
+
573
+			// Find a possible table name for a MM condition.
574
+			$tableNameCondition = $table->field($fieldName)->getAdditionalTableNameCondition();
575
+			if ($tableNameCondition) {
576
+				// If we can find a source file name,  we can then retrieve more MM conditions from the TCA such as a field name.
577
+				$sourceFileName = $this->query->getSourceFieldName();
578
+				if (empty($sourceFileName)) {
579
+					$additionalMMConditions = array(
580
+						'tablenames' => $tableNameCondition,
581
+					);
582
+				} else {
583
+					$additionalMMConditions = Tca::table($tableNameCondition)->field($sourceFileName)->getAdditionalMMCondition();
584
+				}
585
+
586
+				foreach ($additionalMMConditions as $additionalFieldName => $additionalMMCondition) {
587
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
588
+					$statementParts['unions'][$relationTableNameAlias] .= $additionalJoin;
589
+
590
+					$additionalJoin = sprintf(' AND %s.%s = "%s"', $relationTableNameAlias, $additionalFieldName, $additionalMMCondition);
591
+					$statementParts['unions'][$childTableNameAlias] .= $additionalJoin;
592
+				}
593
+			}
594
+		} elseif ($table->field($fieldName)->hasMany()) { // includes relations "many-to-one" and "csv" relations
595
+			$childTableNameAlias = $this->generateAlias($childTableName);
596
+			$this->currentChildTableNameAlias = $childTableNameAlias;
597
+
598
+			if (isset($parentKeyFieldName)) {
599
+				$join = sprintf(
600
+					'LEFT JOIN %s AS %s ON %s.uid=%s.%s',
601
+					$childTableName,
602
+					$childTableNameAlias,
603
+					$tableName,
604
+					$childTableNameAlias,
605
+					$parentKeyFieldName
606
+				);
607
+				$statementParts['unions'][$childTableNameAlias] = $join;
608
+			} else {
609
+				$join = sprintf(
610
+					'LEFT JOIN %s AS %s ON (FIND_IN_SET(%s.uid, %s.%s))',
611
+					$childTableName,
612
+					$childTableNameAlias,
613
+					$childTableNameAlias,
614
+					$tableName,
615
+					$fieldName
616
+				);
617
+				$statementParts['unions'][$childTableNameAlias] = $join;
618
+			}
619
+		} else {
620
+			throw new Exception('Could not determine type of relation.', 1252502725);
621
+		}
622
+
623
+		$statementParts['keywords']['distinct'] = 'DISTINCT';
624
+		$propertyPath = $explodedPropertyPath[1];
625
+		$tableName = $childTableName;
626
+	}
627
+
628
+	/**
629
+	 * Returns the SQL operator for the given JCR operator type.
630
+	 *
631
+	 * @param string $operator One of the JCR_OPERATOR_* constants
632
+	 * @return string an SQL operator
633
+	 * @throws Exception
634
+	 */
635
+	protected function resolveOperator($operator)
636
+	{
637
+		switch ($operator) {
638
+			case self::OPERATOR_EQUAL_TO_NULL:
639
+				$operator = 'IS';
640
+				break;
641
+			case self::OPERATOR_NOT_EQUAL_TO_NULL:
642
+				$operator = 'IS NOT';
643
+				break;
644
+			case QueryInterface::OPERATOR_IN:
645
+				$operator = 'IN';
646
+				break;
647
+			case QueryInterface::OPERATOR_EQUAL_TO:
648
+				$operator = '=';
649
+				break;
650
+			case QueryInterface::OPERATOR_NOT_EQUAL_TO:
651
+				$operator = '!=';
652
+				break;
653
+			case QueryInterface::OPERATOR_LESS_THAN:
654
+				$operator = '<';
655
+				break;
656
+			case QueryInterface::OPERATOR_LESS_THAN_OR_EQUAL_TO:
657
+				$operator = '<=';
658
+				break;
659
+			case QueryInterface::OPERATOR_GREATER_THAN:
660
+				$operator = '>';
661
+				break;
662
+			case QueryInterface::OPERATOR_GREATER_THAN_OR_EQUAL_TO:
663
+				$operator = '>=';
664
+				break;
665
+			case QueryInterface::OPERATOR_LIKE:
666
+				$operator = 'LIKE';
667
+				break;
668
+			default:
669
+				throw new Exception('Unsupported operator encountered.', 1242816073);
670
+		}
671
+		return $operator;
672
+	}
673
+
674
+	/**
675
+	 * Adds additional WHERE statements according to the query settings.
676
+	 *
677
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
678
+	 * @param string $tableNameOrAlias The table name to add the additional where clause for
679
+	 * @param array &$statementParts
680
+	 * @return void
681
+	 */
682
+	protected function addAdditionalWhereClause(QuerySettingsInterface $querySettings, $tableNameOrAlias, &$statementParts)
683
+	{
684
+		$this->addVisibilityConstraintStatement($querySettings, $tableNameOrAlias, $statementParts);
685
+		if ($querySettings->getRespectSysLanguage()) {
686
+			$this->addSysLanguageStatement($tableNameOrAlias, $statementParts, $querySettings);
687
+		}
688
+	}
689
+
690
+	/**
691
+	 * Adds enableFields and deletedClause to the query if necessary
692
+	 *
693
+	 * @param QuerySettingsInterface $querySettings
694
+	 * @param string $tableNameOrAlias The database table name
695
+	 * @param array &$statementParts The query parts
696
+	 * @return void
697
+	 */
698
+	protected function addVisibilityConstraintStatement(QuerySettingsInterface $querySettings, $tableNameOrAlias, array &$statementParts)
699
+	{
700
+		$statement = '';
701
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
702
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
703
+			$ignoreEnableFields = $querySettings->getIgnoreEnableFields();
704
+			$enableFieldsToBeIgnored = $querySettings->getEnableFieldsToBeIgnored();
705
+			$includeDeleted = $querySettings->getIncludeDeleted();
706
+			if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()) {
707
+				$statement .= $this->getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted);
708
+			} else {
709
+				// 'BE' case
710
+				$statement .= $this->getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted);
711
+			}
712
+
713
+			// Remove the prefixing "AND" if any.
714
+			if (!empty($statement)) {
715
+				$statement = strtolower(substr($statement, 1, 3)) === 'and' ? substr($statement, 5) : $statement;
716
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = $statement;
717
+			}
718
+		}
719
+	}
720
+
721
+	/**
722
+	 * Returns constraint statement for frontend context
723
+	 *
724
+	 * @param string $tableNameOrAlias
725
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
726
+	 * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is null or an empty array (default) all enable fields are ignored.
727
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
728
+	 * @return string
729
+	 * @throws Exception\InconsistentQuerySettingsException
730
+	 */
731
+	protected function getFrontendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $enableFieldsToBeIgnored, $includeDeleted)
732
+	{
733
+		$statement = '';
734
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
735
+		if ($ignoreEnableFields && !$includeDeleted) {
736
+			if (count($enableFieldsToBeIgnored)) {
737
+				// array_combine() is necessary because of the way \TYPO3\CMS\Frontend\Page\PageRepository::enableFields() is implemented
738
+				$statement .= $this->getPageRepository()->enableFields($tableName, -1, array_combine($enableFieldsToBeIgnored, $enableFieldsToBeIgnored));
739
+			} else {
740
+				$statement .= $this->getPageRepository()->deleteClause($tableName);
741
+			}
742
+		} elseif (!$ignoreEnableFields && !$includeDeleted) {
743
+			$statement .= $this->getPageRepository()->enableFields($tableName);
744
+		} elseif (!$ignoreEnableFields && $includeDeleted) {
745
+			throw new InconsistentQuerySettingsException('Query setting "ignoreEnableFields=false" can not be used together with "includeDeleted=true" in frontend context.', 1327678173);
746
+		}
747
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
748
+	}
749
+
750
+	/**
751
+	 * Returns constraint statement for backend context
752
+	 *
753
+	 * @param string $tableNameOrAlias
754
+	 * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored
755
+	 * @param boolean $includeDeleted A flag indicating whether deleted records should be included
756
+	 * @return string
757
+	 */
758
+	protected function getBackendConstraintStatement($tableNameOrAlias, $ignoreEnableFields, $includeDeleted)
759
+	{
760
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
761
+		$statement = '';
762
+		if (!$ignoreEnableFields) {
763
+			$statement .= BackendUtility::BEenableFields($tableName);
764
+		}
765
+
766
+		// If the table is found to have "workspace" support, add the corresponding fields in the statement.
767
+		if (Tca::table($tableName)->hasWorkspaceSupport()) {
768
+			if ($this->getBackendUser()->workspace === 0) {
769
+				$statement .= ' AND ' . $tableName . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
770
+			} else {
771
+				// Show only records of live and of the current workspace
772
+				// In case we are in a Versioning preview
773
+				$statement .= ' AND (' .
774
+					$tableName . '.t3ver_wsid=0 OR ' .
775
+					$tableName . '.t3ver_wsid=' . (int)$this->getBackendUser()->workspace .
776
+					')';
777
+			}
778
+
779
+			// Check if this segment make sense here or whether it should be in the "if" part when we have workspace = 0
780
+			$statement .= ' AND ' . $tableName . '.pid<>-1';
781
+		}
782
+
783
+		if (!$includeDeleted) {
784
+			$statement .= BackendUtility::deleteClause($tableName);
785
+		}
786
+
787
+		return $this->replaceTableNameByAlias($tableName, $tableNameOrAlias, $statement);
788
+	}
789
+
790
+	/**
791
+	 * Builds the language field statement
792
+	 *
793
+	 * @param string $tableNameOrAlias The database table name
794
+	 * @param array &$statementParts The query parts
795
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
796
+	 * @return void
797
+	 * @throws Exception
798
+	 */
799
+	protected function addSysLanguageStatement($tableNameOrAlias, array &$statementParts, $querySettings)
800
+	{
801
+		$tableName = $this->resolveTableNameAlias($tableNameOrAlias);
802
+		if (is_array($GLOBALS['TCA'][$tableName]['ctrl'])) {
803
+			if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])) {
804
+				// Select all entries for the current language
805
+				$additionalWhereClause = $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' IN (' . intval($querySettings->getLanguageUid()) . ',-1)';
806
+				// If any language is set -> get those entries which are not translated yet
807
+				// They will be removed by t3lib_page::getRecordOverlay if not matching overlay mode
808
+				if (isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
809
+					&& $querySettings->getLanguageUid() > 0
810
+				) {
811
+					$additionalWhereClause .= ' OR (' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '=0' .
812
+						' AND ' . $tableNameOrAlias . '.uid NOT IN (SELECT ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] .
813
+						' FROM ' . $tableName .
814
+						' WHERE ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'] . '>0' .
815
+						' AND ' . $tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . '>0';
816
+
817
+					// Add delete clause to ensure all entries are loaded
818
+					if (isset($GLOBALS['TCA'][$tableName]['ctrl']['delete'])) {
819
+						$additionalWhereClause .= ' AND ' . $tableNameOrAlias . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] . '=0';
820
+					}
821
+					$additionalWhereClause .= '))';
822
+				}
823
+				$statementParts['additionalWhereClause'][$tableNameOrAlias][] = '(' . $additionalWhereClause . ')';
824
+			}
825
+		}
826
+	}
827
+
828
+	/**
829
+	 * Transforms orderings into SQL.
830
+	 *
831
+	 * @param array $orderings An array of orderings (Tx_Extbase_Persistence_QOM_Ordering)
832
+	 * @param SourceInterface $source The source
833
+	 * @param array &$statementParts The query parts
834
+	 * @return void
835
+	 * @throws Exception\UnsupportedOrderException
836
+	 */
837
+	protected function parseOrderings(array $orderings, SourceInterface $source, array &$statementParts)
838
+	{
839
+		foreach ($orderings as $fieldNameAndPath => $order) {
840
+			switch ($order) {
841
+				case QueryInterface::ORDER_ASCENDING:
842
+					$order = 'ASC';
843
+					break;
844
+				case QueryInterface::ORDER_DESCENDING:
845
+					$order = 'DESC';
846
+					break;
847
+				default:
848
+					throw new UnsupportedOrderException('Unsupported order encountered.', 1456845126);
849
+			}
850
+
851
+			$tableName = $this->getFieldPathResolver()->getDataType($fieldNameAndPath, $this->query->getType());
852
+			$fieldName = $this->getFieldPathResolver()->stripFieldPath($fieldNameAndPath, $tableName);
853
+			$statementParts['orderings'][] = sprintf('%s.%s %s', $tableName, $fieldName, $order);
854
+		}
855
+	}
856
+
857
+	/**
858
+	 * Transforms limit and offset into SQL
859
+	 *
860
+	 * @param int $limit
861
+	 * @param int $offset
862
+	 * @param array &$statementParts
863
+	 * @return void
864
+	 */
865
+	protected function parseLimitAndOffset($limit, $offset, array &$statementParts)
866
+	{
867
+		if ($limit !== null && $offset !== null) {
868
+			$statementParts['limit'] = intval($offset) . ', ' . intval($limit);
869
+		} elseif ($limit !== null) {
870
+			$statementParts['limit'] = intval($limit);
871
+		}
872
+	}
873
+
874
+	/**
875
+	 * @param array $rows
876
+	 * @return array
877
+	 */
878
+	protected function getContentObjects(array $rows): array
879
+	{
880
+		$contentObjects = [];
881
+		foreach ($rows as $row) {
882
+			// Get language uid from querySettings.
883
+			// Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
884
+			$overlaidRow = $this->doLanguageAndWorkspaceOverlay(
885
+				$row,
886
+				$this->query->getTypo3QuerySettings()
887
+			);
888
+
889
+			$contentObjects[] = GeneralUtility::makeInstance(
890
+				Content::class,
891
+				$this->query->getType(),
892
+				$overlaidRow
893
+			);
894
+		}
895
+
896
+		return $contentObjects;
897
+	}
898
+
899
+	/**
900
+	 * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
901
+	 * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
902
+	 *
903
+	 * @param array $row
904
+	 * @param QuerySettingsInterface $querySettings The TYPO3 CMS specific query settings
905
+	 * @return array
906
+	 */
907
+	protected function doLanguageAndWorkspaceOverlay(array $row, $querySettings)
908
+	{
909
+		$tableName = $this->getTableName();
910
+
911
+		$pageRepository = $this->getPageRepository();
912
+		if (is_object($GLOBALS['TSFE'])) {
913
+			$languageMode = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('language', 'legacyLanguageMode');
914
+		#if ($this->isBackendUserLogged() && $this->getBackendUser()->workspace !== 0) {
915
+			#    $pageRepository->versioningWorkspaceId = $this->getBackendUser()->workspace;
916
+		#}
917
+		} else {
918
+			$languageMode = '';
919
+			$workspaceUid = $this->getBackendUser()->workspace;
920
+			#$pageRepository->versioningWorkspaceId = $workspaceUid;
921
+			#if ($this->getBackendUser()->workspace !== 0) {
922
+			#    $pageRepository->versioningPreview = 1;
923
+			#}
924
+		}
925
+
926
+		// If current row is a translation select its parent
927
+		if (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
928
+			&& isset($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
929
+		) {
930
+			if (isset($row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']])
931
+				&& $row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']] > 0
932
+			) {
933
+				$queryBuilder = $this->getQueryBuilder();
934
+				$row = $queryBuilder
935
+					->select($tableName . '.*')
936
+					->from($tableName)
937
+					->andWhere(
938
+						$tableName . '.uid=' . (int)$row[$GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField']],
939
+						$tableName . '.' . $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] . ' = 0'
940
+					)
941
+					->execute()
942
+					->fetch();
943
+			}
944
+		}
945
+
946
+		// Retrieve the original uid; Used for Workspaces!
947
+		if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
948
+			$pageRepository->versionOL($tableName, $row, true, true);
949
+		} else {
950
+			\TYPO3\CMS\Backend\Utility\BackendUtility::workspaceOL($tableName, $row);
951
+		}
952
+		if (isset($row['_ORIG_uid'])) {
953
+			$row['uid'] = $row['_ORIG_uid'];
954
+		}
955
+
956
+		// Special case for table "pages"
957
+		if ($tableName == 'pages') {
958
+			$row = $pageRepository->getPageOverlay($row, $querySettings->getLanguageUid());
959
+		} elseif (isset($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
960
+			&& $GLOBALS['TCA'][$tableName]['ctrl']['languageField'] !== ''
961
+		) {
962
+			if (in_array($row[$GLOBALS['TCA'][$tableName]['ctrl']['languageField']], array(-1, 0))) {
963
+				$overlayMode = $languageMode === 'strict' ? 'hideNonTranslated' : '';
964
+				$row = $pageRepository->getRecordOverlay($tableName, $row, $querySettings->getLanguageUid(), $overlayMode);
965
+			}
966
+		}
967
+
968
+		return $row;
969
+	}
970
+
971
+	/**
972
+	 * Return a resolved table name given a possible table name alias.
973
+	 *
974
+	 * @param string $tableNameOrAlias
975
+	 * @return string
976
+	 */
977
+	protected function resolveTableNameAlias($tableNameOrAlias)
978
+	{
979
+		$resolvedTableName = $tableNameOrAlias;
980
+		if (!empty($this->tableNameAliases['aliases'][$tableNameOrAlias])) {
981
+			$resolvedTableName = $this->tableNameAliases['aliases'][$tableNameOrAlias];
982
+		}
983
+		return $resolvedTableName;
984
+	}
985
+
986
+	/**
987
+	 * Generate a unique table name alias for the given table name.
988
+	 *
989
+	 * @param string $tableName
990
+	 * @return string
991
+	 */
992
+	protected function generateAlias($tableName)
993
+	{
994
+		if (!isset($this->tableNameAliases['aliasIncrement'][$tableName])) {
995
+			$this->tableNameAliases['aliasIncrement'][$tableName] = 0;
996
+		}
997
+
998
+		$numberOfAliases = $this->tableNameAliases['aliasIncrement'][$tableName];
999
+		$tableNameAlias = $tableName . $numberOfAliases;
1000
+
1001
+		$this->tableNameAliases['aliasIncrement'][$tableName]++;
1002
+		$this->tableNameAliases['aliases'][$tableNameAlias] = $tableName;
1003
+
1004
+		return $tableNameAlias;
1005
+	}
1006
+
1007
+	/**
1008
+	 * Replace the table names by its table name alias within the given statement.
1009
+	 *
1010
+	 * @param string $tableName
1011
+	 * @param string $tableNameAlias
1012
+	 * @param string $statement
1013
+	 * @return string
1014
+	 */
1015
+	protected function replaceTableNameByAlias($tableName, $tableNameAlias, $statement)
1016
+	{
1017
+		if ($statement && $tableName !== $tableNameAlias) {
1018
+			$statement = str_replace($tableName, $tableNameAlias, $statement);
1019
+		}
1020
+		return $statement;
1021
+	}
1022
+
1023
+	/**
1024
+	 * Returns an instance of the current Backend User.
1025
+	 *
1026
+	 * @return BackendUserAuthentication
1027
+	 */
1028
+	protected function getBackendUser()
1029
+	{
1030
+		return $GLOBALS['BE_USER'];
1031
+	}
1032
+
1033
+	/**
1034
+	 * Tell whether a Backend User is logged in.
1035
+	 *
1036
+	 * @return bool
1037
+	 */
1038
+	protected function isBackendUserLogged()
1039
+	{
1040
+		return is_object($GLOBALS['BE_USER']);
1041
+	}
1042
+
1043
+	/**
1044
+	 * @return PageRepository|object
1045
+	 */
1046
+	protected function getPageRepository()
1047
+	{
1048
+		if (!$this->pageRepository instanceof PageRepository) {
1049
+			if (ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() && is_object($GLOBALS['TSFE'])) {
1050
+				$this->pageRepository = $GLOBALS['TSFE']->sys_page;
1051
+			} else {
1052
+				$this->pageRepository = GeneralUtility::makeInstance(PageRepository::class);
1053
+			}
1054
+		}
1055
+
1056
+		return $this->pageRepository;
1057
+	}
1058
+
1059
+	/**
1060
+	 * @return FieldPathResolver|object
1061
+	 */
1062
+	protected function getFieldPathResolver()
1063
+	{
1064
+		return GeneralUtility::makeInstance(FieldPathResolver::class);
1065
+	}
1066
+
1067
+	/**
1068
+	 * @return object|Connection
1069
+	 */
1070
+	protected function getConnection(): Connection
1071
+	{
1072
+		/** @var ConnectionPool $connectionPool */
1073
+		return GeneralUtility::makeInstance(ConnectionPool::class)
1074
+			->getConnectionForTable($this->getTableName());
1075
+	}
1076
+
1077
+	/**
1078
+	 * @return object|QueryBuilder
1079
+	 */
1080
+	protected function getQueryBuilder(): QueryBuilder
1081
+	{
1082
+		/** @var ConnectionPool $connectionPool */
1083
+		$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1084
+		return $connectionPool->getQueryBuilderForTable($this->getTableName());
1085
+	}
1086
+
1087
+	/**
1088
+	 * @return string
1089
+	 */
1090
+	public function getTableName(): string
1091
+	{
1092
+		return $this->query->getSource()->getNodeTypeName(); // getSelectorName()
1093
+	}
1094 1094
 }
Please login to merge, or discard this patch.
Classes/Persistence/PagerObjectFactory.php 1 patch
Indentation   +42 added lines, -42 removed lines patch added patch discarded remove patch
@@ -17,46 +17,46 @@
 block discarded – undo
17 17
  */
18 18
 class PagerObjectFactory implements SingletonInterface
19 19
 {
20
-    /**
21
-     * Gets a singleton instance of this class.
22
-     *
23
-     * @return \Fab\Vidi\Persistence\PagerObjectFactory|object
24
-     */
25
-    public static function getInstance()
26
-    {
27
-        return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\PagerObjectFactory::class);
28
-    }
29
-
30
-    /**
31
-     * Returns a pager object.
32
-     *
33
-     * @return Pager
34
-     */
35
-    public function getPager()
36
-    {
37
-        /** @var $pager \Fab\Vidi\Persistence\Pager */
38
-        $pager = GeneralUtility::makeInstance(Pager::class);
39
-
40
-        // Set items per page
41
-        if (GeneralUtility::_GET('length') !== null) {
42
-            $limit = (int)GeneralUtility::_GET('length');
43
-            $pager->setLimit($limit);
44
-        }
45
-
46
-        // Set offset
47
-        $offset = 0;
48
-        if (GeneralUtility::_GET('start') !== null) {
49
-            $offset = (int)GeneralUtility::_GET('start');
50
-        }
51
-        $pager->setOffset($offset);
52
-
53
-        // set page
54
-        $page = 1;
55
-        if ($pager->getLimit() > 0) {
56
-            $page = round($pager->getOffset() / $pager->getLimit());
57
-        }
58
-        $pager->setPage($page);
59
-
60
-        return $pager;
61
-    }
20
+	/**
21
+	 * Gets a singleton instance of this class.
22
+	 *
23
+	 * @return \Fab\Vidi\Persistence\PagerObjectFactory|object
24
+	 */
25
+	public static function getInstance()
26
+	{
27
+		return GeneralUtility::makeInstance(\Fab\Vidi\Persistence\PagerObjectFactory::class);
28
+	}
29
+
30
+	/**
31
+	 * Returns a pager object.
32
+	 *
33
+	 * @return Pager
34
+	 */
35
+	public function getPager()
36
+	{
37
+		/** @var $pager \Fab\Vidi\Persistence\Pager */
38
+		$pager = GeneralUtility::makeInstance(Pager::class);
39
+
40
+		// Set items per page
41
+		if (GeneralUtility::_GET('length') !== null) {
42
+			$limit = (int)GeneralUtility::_GET('length');
43
+			$pager->setLimit($limit);
44
+		}
45
+
46
+		// Set offset
47
+		$offset = 0;
48
+		if (GeneralUtility::_GET('start') !== null) {
49
+			$offset = (int)GeneralUtility::_GET('start');
50
+		}
51
+		$pager->setOffset($offset);
52
+
53
+		// set page
54
+		$page = 1;
55
+		if ($pager->getLimit() > 0) {
56
+			$page = round($pager->getOffset() / $pager->getLimit());
57
+		}
58
+		$pager->setPage($page);
59
+
60
+		return $pager;
61
+	}
62 62
 }
Please login to merge, or discard this patch.