[TASK] Doctrine: Add support for aggregate SQL functions
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Database / Query / QueryBuilderTest.php
1 <?php
2 declare (strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\Database\Query;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Prophecy\Argument;
19 use TYPO3\CMS\Core\Database\Connection;
20 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
21 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
22 use TYPO3\CMS\Core\Tests\Unit\Database\Mocks\MockPlatform;
23 use TYPO3\CMS\Core\Tests\UnitTestCase;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26 class QueryBuilderTest extends UnitTestCase
27 {
28 /**
29 * @var Connection|\Prophecy\Prophecy\ObjectProphecy
30 */
31 protected $connection;
32
33 /**
34 * @var \Doctrine\DBAL\Platforms\AbstractPlatform
35 */
36 protected $platform;
37
38 /**
39 * @var QueryBuilder
40 */
41 protected $subject;
42
43 /**
44 * @var \Doctrine\DBAL\Query\QueryBuilder|\Prophecy\Prophecy\ObjectProphecy
45 */
46 protected $concreteQueryBuilder;
47
48 /**
49 * Create a new database connection mock object for every test.
50 *
51 * @return void
52 */
53 protected function setUp()
54 {
55 parent::setUp();
56
57 $this->concreteQueryBuilder = $this->prophesize(\Doctrine\DBAL\Query\QueryBuilder::class);
58
59 $this->connection = $this->prophesize(Connection::class);
60 $this->connection->getDatabasePlatform()->willReturn(new MockPlatform());
61
62 $this->subject = GeneralUtility::makeInstance(
63 QueryBuilder::class,
64 $this->connection->reveal(),
65 null,
66 $this->concreteQueryBuilder->reveal()
67 );
68 }
69
70 /**
71 * @test
72 */
73 public function exprReturnsExpressionBuilderForConnection()
74 {
75 $this->connection->getExpressionBuilder()
76 ->shouldBeCalled()
77 ->willReturn(GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal()));
78
79 $this->subject->expr();
80 }
81
82 /**
83 * @test
84 */
85 public function getTypeDelegatesToConcreteQueryBuilder()
86 {
87 $this->concreteQueryBuilder->getType()
88 ->shouldBeCalled()
89 ->willReturn(\Doctrine\DBAL\Query\QueryBuilder::INSERT);
90
91 $this->subject->getType();
92 }
93
94 /**
95 * @test
96 */
97 public function getStateDelegatesToConcreteQueryBuilder()
98 {
99 $this->concreteQueryBuilder->getState()
100 ->shouldBeCalled()
101 ->willReturn(\Doctrine\DBAL\Query\QueryBuilder::STATE_CLEAN);
102
103 $this->subject->getState();
104 }
105
106 /**
107 * @test
108 */
109 public function getSQLDelegatesToConcreteQueryBuilder()
110 {
111 $this->concreteQueryBuilder->getSQL()
112 ->shouldBeCalled()
113 ->willReturn('UPDATE aTable SET pid = 7');
114 $this->concreteQueryBuilder->getType()
115 ->willReturn(2); // Update Type
116
117 $this->subject->getSQL();
118 }
119
120 /**
121 * @test
122 */
123 public function setParameterDelegatesToConcreteQueryBuilder()
124 {
125 $this->concreteQueryBuilder->setParameter(Argument::exact('aField'), Argument::exact(5), Argument::cetera())
126 ->shouldBeCalled()
127 ->willReturn($this->subject);
128
129 $this->subject->setParameter('aField', 5);
130 }
131
132 /**
133 * @test
134 */
135 public function setParametersDelegatesToConcreteQueryBuilder()
136 {
137 $this->concreteQueryBuilder->setParameters(Argument::exact(['aField' => 'aValue']), Argument::exact([]))
138 ->shouldBeCalled()
139 ->willReturn($this->subject);
140
141 $this->subject->setParameters(['aField' => 'aValue']);
142 }
143
144 /**
145 * @test
146 */
147 public function getParametersDelegatesToConcreteQueryBuilder()
148 {
149 $this->concreteQueryBuilder->getParameters()
150 ->shouldBeCalled()
151 ->willReturn(['aField' => 'aValue']);
152
153 $this->subject->getParameters();
154 }
155
156 /**
157 * @test
158 */
159 public function getParameterDelegatesToConcreteQueryBuilder()
160 {
161 $this->concreteQueryBuilder->getParameter(Argument::exact('aField'))
162 ->shouldBeCalled()
163 ->willReturn('aValue');
164
165 $this->subject->getParameter('aField');
166 }
167
168 /**
169 * @test
170 */
171 public function getParameterTypesDelegatesToConcreteQueryBuilder()
172 {
173 $this->concreteQueryBuilder->getParameterTypes()
174 ->shouldBeCalled()
175 ->willReturn([]);
176
177 $this->subject->getParameterTypes();
178 }
179
180 /**
181 * @test
182 */
183 public function getParameterTypeDelegatesToConcreteQueryBuilder()
184 {
185 $this->concreteQueryBuilder->getParameterType(Argument::exact('aField'))
186 ->shouldBeCalled()
187 ->willReturn(Connection::PARAM_STR);
188
189 $this->subject->getParameterType('aField');
190 }
191
192 /**
193 * @test
194 */
195 public function setFirstResultDelegatesToConcreteQueryBuilder()
196 {
197 $this->concreteQueryBuilder->setFirstResult(Argument::cetera())
198 ->shouldBeCalled()
199 ->willReturn($this->subject);
200
201 $this->subject->setFirstResult(1);
202 }
203
204 /**
205 * @test
206 */
207 public function getFirstResultDelegatesToConcreteQueryBuilder()
208 {
209 $this->concreteQueryBuilder->getFirstResult()
210 ->shouldBeCalled()
211 ->willReturn(1);
212
213 $this->subject->getFirstResult();
214 }
215
216 /**
217 * @test
218 */
219 public function setMaxResultsDelegatesToConcreteQueryBuilder()
220 {
221 $this->concreteQueryBuilder->setMaxResults(Argument::cetera())
222 ->shouldBeCalled()
223 ->willReturn($this->subject);
224
225 $this->subject->setMaxResults(1);
226 }
227
228 /**
229 * @test
230 */
231 public function getMaxResultsDelegatesToConcreteQueryBuilder()
232 {
233 $this->concreteQueryBuilder->getMaxResults()
234 ->shouldBeCalled()
235 ->willReturn(1);
236
237 $this->subject->getMaxResults();
238 }
239
240 /**
241 * @test
242 */
243 public function addDelegatesToConcreteQueryBuilder()
244 {
245 $this->concreteQueryBuilder->add(Argument::exact('select'), Argument::exact('aField'), Argument::cetera())
246 ->shouldBeCalled()
247 ->willReturn($this->subject);
248
249 $this->subject->add('select', 'aField');
250 }
251
252 /**
253 * @test
254 */
255 public function countBuildsExpressionAndCallsSelect()
256 {
257 $this->concreteQueryBuilder->select(Argument::exact('COUNT(*)'))
258 ->shouldBeCalled()
259 ->willReturn($this->subject);
260
261 $this->subject->count('*');
262 }
263
264 /**
265 * @test
266 */
267 public function selectQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
268 {
269 $this->connection->quoteIdentifier('aField')
270 ->shouldBeCalled()
271 ->willReturnArgument(0);
272 $this->connection->quoteIdentifier('anotherField')
273 ->shouldBeCalled()
274 ->willReturnArgument(0);
275 $this->concreteQueryBuilder->select(Argument::exact('aField'), Argument::exact('anotherField'))
276 ->shouldBeCalled()
277 ->willReturn($this->subject);
278
279 $this->subject->select('aField', 'anotherField');
280 }
281
282 public function quoteIdentifiersForSelectDataProvider()
283 {
284 return [
285 'fieldName' => [
286 'fieldName',
287 '"fieldName"',
288 ],
289 'tableName.fieldName' => [
290 'tableName.fieldName',
291 '"tableName"."fieldName"',
292 ],
293 'tableName.*' => [
294 'tableName.*',
295 '"tableName".*',
296 ],
297 '*' => [
298 '*',
299 '*',
300 ],
301 'fieldName AS anotherFieldName' => [
302 'fieldName AS anotherFieldName',
303 '"fieldName" AS "anotherFieldName"',
304 ],
305 'tableName.fieldName AS anotherFieldName' => [
306 'tableName.fieldName AS anotherFieldName',
307 '"tableName"."fieldName" AS "anotherFieldName"',
308 ],
309 'tableName.fieldName AS anotherTable.anotherFieldName' => [
310 'tableName.fieldName AS anotherTable.anotherFieldName',
311 '"tableName"."fieldName" AS "anotherTable"."anotherFieldName"',
312 ],
313 ];
314 }
315
316 /**
317 * @test
318 * @dataProvider quoteIdentifiersForSelectDataProvider
319 * @param string $identifier
320 * @param string $expectedResult
321 */
322 public function quoteIdentifiersForSelect($identifier, $expectedResult)
323 {
324 $this->connection->quoteIdentifier(Argument::cetera())->will(
325 function ($args) {
326 $platform = new MockPlatform();
327
328 return $platform->quoteIdentifier($args[0]);
329 }
330 );
331
332 $this->assertSame([$expectedResult], $this->subject->quoteIdentifiersForSelect([$identifier]));
333 }
334
335 /**
336 * @test
337 * @expectedException \InvalidArgumentException
338 */
339 public function quoteIdentifiersForSelectWithInvalidAlias()
340 {
341 $this->connection->quoteIdentifier(Argument::cetera())->will(
342 function ($args) {
343 $platform = new MockPlatform();
344
345 return $platform->quoteIdentifier($args[0]);
346 }
347 );
348 $this->subject->quoteIdentifiersForSelect(['aField AS anotherField,someField AS someThing']);
349 }
350
351 /**
352 * @test
353 */
354 public function selectDoesNotQuoteStarPlaceholder()
355 {
356 $this->connection->quoteIdentifier('aField')
357 ->shouldBeCalled()
358 ->willReturnArgument(0);
359 $this->connection->quoteIdentifier('*')
360 ->shouldNotBeCalled();
361 $this->concreteQueryBuilder->select(Argument::exact('aField'), Argument::exact('*'))
362 ->shouldBeCalled()
363 ->willReturn($this->subject);
364
365 $this->subject->select('aField', '*');
366 }
367
368 /**
369 * @test
370 */
371 public function addSelectQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
372 {
373 $this->connection->quoteIdentifier('aField')
374 ->shouldBeCalled()
375 ->willReturnArgument(0);
376 $this->connection->quoteIdentifier('anotherField')
377 ->shouldBeCalled()
378 ->willReturnArgument(0);
379 $this->concreteQueryBuilder->addSelect(Argument::exact('aField'), Argument::exact('anotherField'))
380 ->shouldBeCalled()
381 ->willReturn($this->subject);
382
383 $this->subject->addSelect('aField', 'anotherField');
384 }
385
386 /**
387 * @test
388 */
389 public function addSelectDoesNotQuoteStarPlaceholder()
390 {
391 $this->connection->quoteIdentifier('aField')
392 ->shouldBeCalled()
393 ->willReturnArgument(0);
394 $this->connection->quoteIdentifier('*')
395 ->shouldNotBeCalled();
396 $this->concreteQueryBuilder->addSelect(Argument::exact('aField'), Argument::exact('*'))
397 ->shouldBeCalled()
398 ->willReturn($this->subject);
399
400 $this->subject->addSelect('aField', '*');
401 }
402
403 /**
404 * @test
405 */
406 public function selectLiteralDirectlyDelegatesToConcreteQueryBuilder()
407 {
408 $this->connection->quoteIdentifier(Argument::cetera())
409 ->shouldNotBeCalled();
410 $this->concreteQueryBuilder->select(Argument::exact('MAX(aField) AS anAlias'))
411 ->shouldBeCalled()
412 ->willReturn($this->subject);
413
414 $this->subject->selectLiteral('MAX(aField) AS anAlias');
415 }
416
417 /**
418 * @test
419 */
420 public function addSelectLiteralDirectlyDelegatesToConcreteQueryBuilder()
421 {
422 $this->connection->quoteIdentifier(Argument::cetera())
423 ->shouldNotBeCalled();
424 $this->concreteQueryBuilder->addSelect(Argument::exact('MAX(aField) AS anAlias'))
425 ->shouldBeCalled()
426 ->willReturn($this->subject);
427
428 $this->subject->addSelectLiteral('MAX(aField) AS anAlias');
429 }
430
431 /**
432 * @test
433 * @todo: Test with alias
434 */
435 public function deleteQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
436 {
437 $this->connection->quoteIdentifier('aTable')
438 ->shouldBeCalled()
439 ->willReturnArgument(0);
440 $this->concreteQueryBuilder->delete(Argument::exact('aTable'), Argument::cetera())
441 ->shouldBeCalled()
442 ->willReturn($this->subject);
443
444 $this->subject->delete('aTable');
445 }
446
447 /**
448 * @test
449 * @todo: Test with alias
450 */
451 public function updateQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
452 {
453 $this->connection->quoteIdentifier('aTable')
454 ->shouldBeCalled()
455 ->willReturnArgument(0);
456 $this->concreteQueryBuilder->update(Argument::exact('aTable'), Argument::cetera())
457 ->shouldBeCalled()
458 ->willReturn($this->subject);
459
460 $this->subject->update('aTable');
461 }
462
463 /**
464 * @test
465 */
466 public function insertQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
467 {
468 $this->connection->quoteIdentifier('aTable')
469 ->shouldBeCalled()
470 ->willReturnArgument(0);
471 $this->concreteQueryBuilder->insert(Argument::exact('aTable'))
472 ->shouldBeCalled()
473 ->willReturn($this->subject);
474
475 $this->subject->insert('aTable');
476 }
477
478 /**
479 * @test
480 * @todo: Test with alias
481 */
482 public function fromQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
483 {
484 $this->connection->quoteIdentifier('aTable')
485 ->shouldBeCalled()
486 ->willReturnArgument(0);
487 $this->concreteQueryBuilder->from(Argument::exact('aTable'), Argument::cetera())
488 ->shouldBeCalled()
489 ->willReturn($this->subject);
490
491 $this->subject->from('aTable');
492 }
493
494 /**
495 * @test
496 */
497 public function joinQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
498 {
499 $this->connection->quoteIdentifier('fromAlias')
500 ->shouldBeCalled()
501 ->willReturnArgument(0);
502 $this->connection->quoteIdentifier('join')
503 ->shouldBeCalled()
504 ->willReturnArgument(0);
505 $this->connection->quoteIdentifier('alias')
506 ->shouldBeCalled()
507 ->willReturnArgument(0);
508 $this->concreteQueryBuilder->innerJoin('fromAlias', 'join', 'alias', null)
509 ->shouldBeCalled()
510 ->willReturn($this->subject);
511
512 $this->subject->join('fromAlias', 'join', 'alias');
513 }
514
515 /**
516 * @test
517 */
518 public function innerJoinQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
519 {
520 $this->connection->quoteIdentifier('fromAlias')
521 ->shouldBeCalled()
522 ->willReturnArgument(0);
523 $this->connection->quoteIdentifier('join')
524 ->shouldBeCalled()
525 ->willReturnArgument(0);
526 $this->connection->quoteIdentifier('alias')
527 ->shouldBeCalled()
528 ->willReturnArgument(0);
529 $this->concreteQueryBuilder->innerJoin('fromAlias', 'join', 'alias', null)
530 ->shouldBeCalled()
531 ->willReturn($this->subject);
532
533 $this->subject->innerJoin('fromAlias', 'join', 'alias');
534 }
535
536 /**
537 * @test
538 */
539 public function leftJoinQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
540 {
541 $this->connection->quoteIdentifier('fromAlias')
542 ->shouldBeCalled()
543 ->willReturnArgument(0);
544 $this->connection->quoteIdentifier('join')
545 ->shouldBeCalled()
546 ->willReturnArgument(0);
547 $this->connection->quoteIdentifier('alias')
548 ->shouldBeCalled()
549 ->willReturnArgument(0);
550 $this->concreteQueryBuilder->leftJoin('fromAlias', 'join', 'alias', null)
551 ->shouldBeCalled()
552 ->willReturn($this->subject);
553
554 $this->subject->leftJoin('fromAlias', 'join', 'alias');
555 }
556
557 /**
558 * @test
559 */
560 public function rightJoinQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
561 {
562 $this->connection->quoteIdentifier('fromAlias')
563 ->shouldBeCalled()
564 ->willReturnArgument(0);
565 $this->connection->quoteIdentifier('join')
566 ->shouldBeCalled()
567 ->willReturnArgument(0);
568 $this->connection->quoteIdentifier('alias')
569 ->shouldBeCalled()
570 ->willReturnArgument(0);
571 $this->concreteQueryBuilder->rightJoin('fromAlias', 'join', 'alias', null)
572 ->shouldBeCalled()
573 ->willReturn($this->subject);
574
575 $this->subject->rightJoin('fromAlias', 'join', 'alias');
576 }
577
578 /**
579 * @test
580 */
581 public function setQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
582 {
583 $this->connection->quoteIdentifier('aField')
584 ->shouldBeCalled()
585 ->willReturnArgument(0);
586 $this->concreteQueryBuilder->createNamedParameter('aValue', Argument::cetera())
587 ->shouldBeCalled()
588 ->willReturn(':dcValue1');
589 $this->concreteQueryBuilder->set('aField', ':dcValue1')
590 ->shouldBeCalled()
591 ->willReturn($this->subject);
592
593 $this->subject->set('aField', 'aValue');
594 }
595
596 /**
597 * @test
598 */
599 public function setWithoutNamedParameterQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
600 {
601 $this->connection->quoteIdentifier('aField')
602 ->shouldBeCalled()
603 ->willReturnArgument(0);
604 $this->concreteQueryBuilder->createNamedParameter(Argument::cetera())->shouldNotBeCalled();
605 $this->concreteQueryBuilder->set('aField', 'aValue')
606 ->shouldBeCalled()
607 ->willReturn($this->subject);
608
609 $this->subject->set('aField', 'aValue', false);
610 }
611
612 /**
613 * @test
614 */
615 public function whereDelegatesToConcreteQueryBuilder()
616 {
617 $this->concreteQueryBuilder->where('uid=1', 'type=9')
618 ->shouldBeCalled()
619 ->willReturn($this->subject);
620
621 $this->subject->where('uid=1', 'type=9');
622 }
623
624 /**
625 * @test
626 */
627 public function andWhereDelegatesToConcreteQueryBuilder()
628 {
629 $this->concreteQueryBuilder->andWhere('uid=1', 'type=9')
630 ->shouldBeCalled()
631 ->willReturn($this->subject);
632
633 $this->subject->andWhere('uid=1', 'type=9');
634 }
635
636 /**
637 * @test
638 */
639 public function orWhereDelegatesToConcreteQueryBuilder()
640 {
641 $this->concreteQueryBuilder->orWhere('uid=1', 'type=9')
642 ->shouldBeCalled()
643 ->willReturn($this->subject);
644
645 $this->subject->orWhere('uid=1', 'type=9');
646 }
647
648 /**
649 * @test
650 */
651 public function groupByQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
652 {
653 $this->connection->quoteIdentifiers(['aField', 'anotherField'])
654 ->shouldBeCalled()
655 ->willReturnArgument(0);
656 $this->concreteQueryBuilder->groupBy('aField', 'anotherField')
657 ->shouldBeCalled()
658 ->willReturn($this->subject);
659
660 $this->subject->groupBy('aField', 'anotherField');
661 }
662
663 /**
664 * @test
665 */
666 public function addGroupByQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
667 {
668 $this->connection->quoteIdentifiers(['aField', 'anotherField'])
669 ->shouldBeCalled()
670 ->willReturnArgument(0);
671 $this->concreteQueryBuilder->addGroupBy('aField', 'anotherField')
672 ->shouldBeCalled()
673 ->willReturn($this->subject);
674
675 $this->subject->addGroupBy('aField', 'anotherField');
676 }
677
678 /**
679 * @test
680 */
681 public function setValueQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
682 {
683 $this->connection->quoteIdentifier('aField')
684 ->shouldBeCalled()
685 ->willReturnArgument(0);
686 $this->concreteQueryBuilder->createNamedParameter('aValue', Argument::cetera())
687 ->shouldBeCalled()
688 ->willReturn(':dcValue1');
689 $this->concreteQueryBuilder->setValue('aField', ':dcValue1')
690 ->shouldBeCalled()
691 ->willReturn($this->subject);
692
693 $this->subject->setValue('aField', 'aValue');
694 }
695
696 /**
697 * @test
698 */
699 public function setValueWithoudNamedParameterQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
700 {
701 $this->connection->quoteIdentifier('aField')
702 ->shouldBeCalled()
703 ->willReturnArgument(0);
704 $this->concreteQueryBuilder->setValue('aField', 'aValue')
705 ->shouldBeCalled()
706 ->willReturn($this->subject);
707
708 $this->subject->setValue('aField', 'aValue', false);
709 }
710
711 /**
712 * @test
713 */
714 public function valuesQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
715 {
716 $this->connection->quoteColumnValuePairs(['aField' => ':dcValue1', 'aValue' => ':dcValue2'])
717 ->shouldBeCalled()
718 ->willReturnArgument(0);
719 $this->concreteQueryBuilder->createNamedParameter(1, Argument::cetera())
720 ->shouldBeCalled()
721 ->willReturn(':dcValue1');
722 $this->concreteQueryBuilder->createNamedParameter(2, Argument::cetera())
723 ->shouldBeCalled()
724 ->willReturn(':dcValue2');
725 $this->concreteQueryBuilder->values(['aField' => ':dcValue1', 'aValue' => ':dcValue2'])
726 ->shouldBeCalled()
727 ->willReturn($this->subject);
728
729 $this->subject->values(['aField' => 1, 'aValue' => 2]);
730 }
731
732 /**
733 * @test
734 */
735 public function valuesWithoutNamedParametersQuotesIdentifiersAndDelegatesToConcreteQueryBuilder()
736 {
737 $this->connection->quoteColumnValuePairs(['aField' => 1, 'aValue' => 2])
738 ->shouldBeCalled()
739 ->willReturnArgument(0);
740 $this->concreteQueryBuilder->values(['aField' => 1, 'aValue' => 2])
741 ->shouldBeCalled()
742 ->willReturn($this->subject);
743
744 $this->subject->values(['aField' => 1, 'aValue' => 2], false);
745 }
746
747 /**
748 * @test
749 */
750 public function havingDelegatesToConcreteQueryBuilder()
751 {
752 $this->concreteQueryBuilder->having('uid=1', 'type=9')
753 ->shouldBeCalled()
754 ->willReturn($this->subject);
755
756 $this->subject->having('uid=1', 'type=9');
757 }
758
759 /**
760 * @test
761 */
762 public function andHavingDelegatesToConcreteQueryBuilder()
763 {
764 $this->concreteQueryBuilder->andHaving('uid=1', 'type=9')
765 ->shouldBeCalled()
766 ->willReturn($this->subject);
767
768 $this->subject->andHaving('uid=1', 'type=9');
769 }
770
771 /**
772 * @test
773 */
774 public function orHavingDelegatesToConcreteQueryBuilder()
775 {
776 $this->concreteQueryBuilder->orHaving('uid=1', 'type=9')
777 ->shouldBeCalled()
778 ->willReturn($this->subject);
779
780 $this->subject->orHaving('uid=1', 'type=9');
781 }
782
783 /**
784 * @test
785 */
786 public function orderByQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
787 {
788 $this->connection->quoteIdentifier('aField')
789 ->shouldBeCalled()
790 ->willReturnArgument(0);
791 $this->concreteQueryBuilder->orderBy('aField', null)
792 ->shouldBeCalled()
793 ->willReturn($this->subject);
794
795 $this->subject->orderBy('aField');
796 }
797
798 /**
799 * @test
800 */
801 public function addOrderByQuotesIdentifierAndDelegatesToConcreteQueryBuilder()
802 {
803 $this->connection->quoteIdentifier('aField')
804 ->shouldBeCalled()
805 ->willReturnArgument(0);
806 $this->concreteQueryBuilder->addOrderBy('aField', 'DESC')
807 ->shouldBeCalled()
808 ->willReturn($this->subject);
809
810 $this->subject->addOrderBy('aField', 'DESC');
811 }
812
813 /**
814 * @test
815 */
816 public function getQueryPartDelegatesToConcreteQueryBuilder()
817 {
818 $this->concreteQueryBuilder->getQueryPart('from')
819 ->shouldBeCalled()
820 ->willReturn('aTable');
821
822 $this->subject->getQueryPart('from');
823 }
824
825 /**
826 * @test
827 */
828 public function getQueryPartsDelegatesToConcreteQueryBuilder()
829 {
830 $this->concreteQueryBuilder->getQueryParts()
831 ->shouldBeCalled()
832 ->willReturn([]);
833
834 $this->subject->getQueryParts();
835 }
836
837 /**
838 * @test
839 */
840 public function resetQueryPartsDelegatesToConcreteQueryBuilder()
841 {
842 $this->concreteQueryBuilder->resetQueryParts(['select', 'from'])
843 ->shouldBeCalled()
844 ->willReturn($this->subject);
845
846 $this->subject->resetQueryParts(['select', 'from']);
847 }
848
849 /**
850 * @test
851 */
852 public function resetQueryPartDelegatesToConcreteQueryBuilder()
853 {
854 $this->concreteQueryBuilder->resetQueryPart('select')
855 ->shouldBeCalled()
856 ->willReturn($this->subject);
857
858 $this->subject->resetQueryPart('select');
859 }
860
861 /**
862 * @test
863 */
864 public function createNamedParameterDelegatesToConcreteQueryBuilder()
865 {
866 $this->concreteQueryBuilder->createNamedParameter(5, Argument::cetera())
867 ->shouldBeCalled()
868 ->willReturn(':dcValue1');
869
870 $this->subject->createNamedParameter(5);
871 }
872
873 /**
874 * @test
875 */
876 public function createPositionalParameterDelegatesToConcreteQueryBuilder()
877 {
878 $this->concreteQueryBuilder->createPositionalParameter(5, Argument::cetera())
879 ->shouldBeCalled()
880 ->willReturn('?');
881
882 $this->subject->createPositionalParameter(5);
883 }
884
885 /**
886 * @test
887 */
888 public function queryRestrictionsAreAddedForSelectOnExecute()
889 {
890 $GLOBALS['TCA']['pages']['ctrl'] = [
891 'tstamp' => 'tstamp',
892 'versioningWS' => true,
893 'delete' => 'deleted',
894 'crdate' => 'crdate',
895 'enablecolumns' => [
896 'disabled' => 'hidden',
897 ],
898 ];
899
900 $this->connection->quoteIdentifier(Argument::cetera())
901 ->willReturnArgument(0);
902 $this->connection->quoteIdentifiers(Argument::cetera())
903 ->willReturnArgument(0);
904
905 $connectionBuilder = GeneralUtility::makeInstance(
906 \Doctrine\DBAL\Query\QueryBuilder::class,
907 $this->connection->reveal()
908 );
909
910 $expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal());
911 $this->connection->getExpressionBuilder()
912 ->willReturn($expressionBuilder);
913
914 $subject = GeneralUtility::makeInstance(
915 QueryBuilder::class,
916 $this->connection->reveal(),
917 null,
918 $connectionBuilder
919 );
920
921 $subject->select('*')
922 ->from('pages')
923 ->where('uid=1');
924
925 $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))';
926 $this->connection->executeQuery($expectedSQL, Argument::cetera())
927 ->shouldBeCalled();
928
929 $subject->execute();
930 }
931
932 /**
933 * @test
934 */
935 public function queryRestrictionsAreAddedForCountOnExecute()
936 {
937 $GLOBALS['TCA']['pages']['ctrl'] = [
938 'tstamp' => 'tstamp',
939 'versioningWS' => true,
940 'delete' => 'deleted',
941 'crdate' => 'crdate',
942 'enablecolumns' => [
943 'disabled' => 'hidden',
944 ],
945 ];
946
947 $this->connection->quoteIdentifier(Argument::cetera())
948 ->willReturnArgument(0);
949 $this->connection->quoteIdentifiers(Argument::cetera())
950 ->willReturnArgument(0);
951
952 $connectionBuilder = GeneralUtility::makeInstance(
953 \Doctrine\DBAL\Query\QueryBuilder::class,
954 $this->connection->reveal()
955 );
956
957 $expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal());
958 $this->connection->getExpressionBuilder()
959 ->willReturn($expressionBuilder);
960
961 $subject = GeneralUtility::makeInstance(
962 QueryBuilder::class,
963 $this->connection->reveal(),
964 null,
965 $connectionBuilder
966 );
967
968 $subject->count('uid')
969 ->from('pages')
970 ->where('uid=1');
971
972 $expectedSQL = 'SELECT COUNT(uid) FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))';
973 $this->connection->executeQuery($expectedSQL, Argument::cetera())
974 ->shouldBeCalled();
975
976 $subject->execute();
977 }
978
979 /**
980 * @test
981 */
982 public function queryRestrictionsAreReevaluatedOnSettingsChangeForGetSQL()
983 {
984 $GLOBALS['TCA']['pages']['ctrl'] = [
985 'tstamp' => 'tstamp',
986 'versioningWS' => true,
987 'delete' => 'deleted',
988 'crdate' => 'crdate',
989 'enablecolumns' => [
990 'disabled' => 'hidden',
991 ],
992 ];
993
994 $this->connection->quoteIdentifier(Argument::cetera())
995 ->willReturnArgument(0);
996 $this->connection->quoteIdentifiers(Argument::cetera())
997 ->willReturnArgument(0);
998 $this->connection->getExpressionBuilder()
999 ->willReturn(GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal()));
1000
1001 $concreteQueryBuilder = GeneralUtility::makeInstance(
1002 \Doctrine\DBAL\Query\QueryBuilder::class,
1003 $this->connection->reveal()
1004 );
1005
1006 $subject = GeneralUtility::makeInstance(
1007 QueryBuilder::class,
1008 $this->connection->reveal(),
1009 null,
1010 $concreteQueryBuilder
1011 );
1012
1013 $subject->select('*')
1014 ->from('pages')
1015 ->where('uid=1');
1016
1017 $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))';
1018 $this->assertSame($expectedSQL, $subject->getSQL());
1019
1020 $subject->getQueryContext()
1021 ->setIgnoreEnableFields(true)
1022 ->setIgnoredEnableFields(['disabled']);
1023
1024 $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND (pages.deleted = 0)';
1025 $this->assertSame($expectedSQL, $subject->getSQL());
1026 }
1027
1028 /**
1029 * @test
1030 */
1031 public function queryRestrictionsAreReevaluatedOnSettingsChangeForExecute()
1032 {
1033 $GLOBALS['TCA']['pages']['ctrl'] = [
1034 'tstamp' => 'tstamp',
1035 'versioningWS' => true,
1036 'delete' => 'deleted',
1037 'crdate' => 'crdate',
1038 'enablecolumns' => [
1039 'disabled' => 'hidden',
1040 ],
1041 ];
1042
1043 $this->connection->quoteIdentifier(Argument::cetera())
1044 ->willReturnArgument(0);
1045 $this->connection->quoteIdentifiers(Argument::cetera())
1046 ->willReturnArgument(0);
1047 $this->connection->getExpressionBuilder()
1048 ->willReturn(GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal()));
1049
1050 $concreteQueryBuilder = GeneralUtility::makeInstance(
1051 \Doctrine\DBAL\Query\QueryBuilder::class,
1052 $this->connection->reveal()
1053 );
1054
1055 $subject = GeneralUtility::makeInstance(
1056 QueryBuilder::class,
1057 $this->connection->reveal(),
1058 null,
1059 $concreteQueryBuilder
1060 );
1061
1062 $subject->select('*')
1063 ->from('pages')
1064 ->where('uid=1');
1065
1066 $subject->getQueryContext()
1067 ->setIgnoreEnableFields(true)
1068 ->setIgnoredEnableFields(['disabled']);
1069
1070 $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND (pages.deleted = 0)';
1071 $this->connection->executeQuery($expectedSQL, Argument::cetera())
1072 ->shouldBeCalled();
1073
1074 $subject->execute();
1075
1076 $subject->getQueryContext()
1077 ->setIgnoreEnableFields(false);
1078
1079 $expectedSQL = 'SELECT * FROM pages WHERE (uid=1) AND ((pages.hidden = 0) AND (pages.deleted = 0))';
1080 $this->connection->executeQuery($expectedSQL, Argument::cetera())
1081 ->shouldBeCalled();
1082
1083 $subject->execute();
1084 }
1085 }