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