Completed
Push — 6.13 ( 2b8797...552b6b )
by
unknown
17:12
created

testSortingByNumericFieldsWithValuesOfDifferentLength()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the SearchServiceTest class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Tests;
10
11
use eZ\Publish\API\Repository\Tests\SetupFactory\LegacyElasticsearch;
12
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
13
use eZ\Publish\API\Repository\Values\Content\Content;
14
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
15
use eZ\Publish\API\Repository\Values\Content\Query;
16
use eZ\Publish\API\Repository\Values\Content\Location;
17
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
18
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
19
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
20
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
21
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
22
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
23
use function count;
24
25
/**
26
 * Test case for operations in the SearchService.
27
 *
28
 * @see eZ\Publish\API\Repository\SearchService
29
 * @group integration
30
 * @group search
31
 */
32
class SearchServiceTest extends BaseTest
33
{
34
    const QUERY_CLASS = Query::class;
35
36
    use Common\FacetedSearchProvider;
37
38
    public function getFilterContentSearches()
39
    {
40
        $fixtureDir = $this->getFixtureDir();
41
42
        return [
43
            0 => [
44
                [
45
                    'filter' => new Criterion\ContentId(
46
                        [1, 4, 10]
47
                    ),
48
                    'sortClauses' => [new SortClause\ContentId()],
49
                ],
50
                $fixtureDir . 'ContentId.php',
51
            ],
52
            1 => [
53
                [
54
                    'filter' => new Criterion\LogicalAnd(
55
                        [
56
                            new Criterion\ContentId(
57
                                [1, 4, 10]
58
                            ),
59
                            new Criterion\ContentId(
60
                                [4, 12]
61
                            ),
62
                        ]
63
                    ),
64
                    'sortClauses' => [new SortClause\ContentId()],
65
                ],
66
                $fixtureDir . 'LogicalAnd.php',
67
            ],
68
            2 => [
69
                [
70
                    'filter' => new Criterion\LogicalOr(
71
                        [
72
                            new Criterion\ContentId(
73
                                [1, 4, 10]
74
                            ),
75
                            new Criterion\ContentId(
76
                                [4, 12]
77
                            ),
78
                        ]
79
                    ),
80
                    'sortClauses' => [new SortClause\ContentId()],
81
                ],
82
                $fixtureDir . 'LogicalOr.php',
83
            ],
84
            3 => [
85
                [
86
                    'filter' => new Criterion\LogicalAnd(
87
                        [
88
                            new Criterion\ContentId(
89
                                [1, 4, 10]
90
                            ),
91
                            new Criterion\LogicalNot(
92
                                new Criterion\ContentId(
93
                                    [10, 12]
94
                                )
95
                            ),
96
                        ]
97
                    ),
98
                    'sortClauses' => [new SortClause\ContentId()],
99
                ],
100
                $fixtureDir . 'LogicalNot.php',
101
            ],
102
            4 => [
103
                [
104
                    'filter' => new Criterion\LogicalAnd(
105
                        [
106
                            new Criterion\ContentId(
107
                                [1, 4, 10]
108
                            ),
109
                            new Criterion\LogicalAnd(
110
                                [
111
                                    new Criterion\LogicalNot(
112
                                        new Criterion\ContentId(
113
                                            [10, 12]
114
                                        )
115
                                    ),
116
                                ]
117
                            ),
118
                        ]
119
                    ),
120
                    'sortClauses' => [new SortClause\ContentId()],
121
                ],
122
                $fixtureDir . 'LogicalNot.php',
123
            ],
124
            5 => [
125
                [
126
                    'filter' => new Criterion\ContentTypeId(
127
                        4
128
                    ),
129
                    'sortClauses' => [new SortClause\ContentId()],
130
                ],
131
                $fixtureDir . 'ContentTypeId.php',
132
            ],
133
            6 => [
134
                [
135
                    'filter' => new Criterion\ContentTypeIdentifier(
136
                        'user'
137
                    ),
138
                    'sortClauses' => [new SortClause\ContentId()],
139
                ],
140
                $fixtureDir . 'ContentTypeId.php',
141
            ],
142
            7 => [
143
                [
144
                    'filter' => new Criterion\MatchNone(),
145
                    'sortClauses' => [new SortClause\ContentId()],
146
                ],
147
                $fixtureDir . 'MatchNone.php',
148
            ],
149
            8 => [
150
                [
151
                    'filter' => new Criterion\ContentTypeGroupId(
152
                        2
153
                    ),
154
                    'sortClauses' => [new SortClause\ContentId()],
155
                ],
156
                $fixtureDir . 'ContentTypeGroupId.php',
157
            ],
158
            9 => [
159
                [
160
                    'filter' => new Criterion\DateMetadata(
161
                        Criterion\DateMetadata::MODIFIED,
162
                        Criterion\Operator::GT,
163
                        1343140540
164
                    ),
165
                    'sortClauses' => [new SortClause\ContentId()],
166
                ],
167
                $fixtureDir . 'DateMetadataGt.php',
168
            ],
169
            10 => [
170
                [
171
                    'filter' => new Criterion\DateMetadata(
172
                        Criterion\DateMetadata::MODIFIED,
173
                        Criterion\Operator::GTE,
174
                        1311154215
175
                    ),
176
                    'sortClauses' => [new SortClause\ContentId()],
177
                ],
178
                $fixtureDir . 'DateMetadataGte.php',
179
            ],
180
            11 => [
181
                [
182
                    'filter' => new Criterion\DateMetadata(
183
                        Criterion\DateMetadata::MODIFIED,
184
                        Criterion\Operator::LTE,
185
                        1311154215
186
                    ),
187
                    'limit' => 10,
188
                    'sortClauses' => [new SortClause\ContentId()],
189
                ],
190
                $fixtureDir . 'DateMetadataLte.php',
191
            ],
192
            12 => [
193
                [
194
                    'filter' => new Criterion\DateMetadata(
195
                        Criterion\DateMetadata::MODIFIED,
196
                        Criterion\Operator::IN,
197
                        [1033920794, 1060695457, 1343140540]
198
                    ),
199
                    'sortClauses' => [new SortClause\ContentId()],
200
                ],
201
                $fixtureDir . 'DateMetadataIn.php',
202
            ],
203
            13 => [
204
                [
205
                    'filter' => new Criterion\DateMetadata(
206
                        Criterion\DateMetadata::MODIFIED,
207
                        Criterion\Operator::BETWEEN,
208
                        [1033920776, 1072180276]
209
                    ),
210
                    'sortClauses' => [new SortClause\ContentId()],
211
                ],
212
                $fixtureDir . 'DateMetadataBetween.php',
213
            ],
214
            14 => [
215
                [
216
                    'filter' => new Criterion\DateMetadata(
217
                        Criterion\DateMetadata::CREATED,
218
                        Criterion\Operator::BETWEEN,
219
                        [1033920776, 1072180278]
220
                    ),
221
                    'sortClauses' => [new SortClause\ContentId()],
222
                ],
223
                $fixtureDir . 'DateMetadataCreated.php',
224
            ],
225
            15 => [
226
                [
227
                    'filter' => new Criterion\CustomField(
228
                        'user_group_name_value_s',
229
                        Criterion\Operator::EQ,
230
                        'Members'
231
                    ),
232
                    'sortClauses' => [new SortClause\ContentId()],
233
                ],
234
                $fixtureDir . 'Field.php',
235
            ],
236
            16 => [
237
                [
238
                    'filter' => new Criterion\CustomField(
239
                        'user_group_name_value_s',
240
                        Criterion\Operator::CONTAINS,
241
                        'Members'
242
                    ),
243
                    'sortClauses' => [new SortClause\ContentId()],
244
                ],
245
                $fixtureDir . 'Field.php',
246
            ],
247
            17 => [
248
                [
249
                    'filter' => new Criterion\CustomField(
250
                        'user_group_name_value_s',
251
                        Criterion\Operator::LT,
252
                        'Members'
253
                    ),
254
                    'sortClauses' => [new SortClause\ContentId()],
255
                ],
256
                $fixtureDir . 'CustomFieldLt.php',
257
            ],
258
            18 => [
259
                [
260
                    'filter' => new Criterion\CustomField(
261
                        'user_group_name_value_s',
262
                        Criterion\Operator::LTE,
263
                        'Members'
264
                    ),
265
                    'sortClauses' => [new SortClause\ContentId()],
266
                ],
267
                $fixtureDir . 'CustomFieldLte.php',
268
            ],
269
            19 => [
270
                [
271
                    'filter' => new Criterion\CustomField(
272
                        'user_group_name_value_s',
273
                        Criterion\Operator::GT,
274
                        'Members'
275
                    ),
276
                    'sortClauses' => [new SortClause\ContentId()],
277
                ],
278
                $fixtureDir . 'CustomFieldGt.php',
279
            ],
280
            20 => [
281
                [
282
                    'filter' => new Criterion\CustomField(
283
                        'user_group_name_value_s',
284
                        Criterion\Operator::GTE,
285
                        'Members'
286
                    ),
287
                    'sortClauses' => [new SortClause\ContentId()],
288
                ],
289
                $fixtureDir . 'CustomFieldGte.php',
290
            ],
291
            21 => [
292
                [
293
                    'filter' => new Criterion\CustomField(
294
                        'user_group_name_value_s',
295
                        Criterion\Operator::BETWEEN,
296
                        ['Administrator users', 'Members']
297
                    ),
298
                    'sortClauses' => [new SortClause\ContentId()],
299
                ],
300
                $fixtureDir . 'CustomFieldBetween.php',
301
            ],
302
            22 => [
303
                [
304
                    'filter' => new Criterion\RemoteId(
305
                        ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca']
306
                    ),
307
                    'sortClauses' => [new SortClause\ContentId()],
308
                ],
309
                $fixtureDir . 'RemoteId.php',
310
            ],
311
            23 => [
312
                [
313
                    'filter' => new Criterion\SectionId(
314
                        [2]
315
                    ),
316
                    'sortClauses' => [new SortClause\ContentId()],
317
                ],
318
                $fixtureDir . 'SectionId.php',
319
            ],
320
            24 => [
321
                [
322
                    'filter' => new Criterion\Field(
323
                        'name',
324
                        Criterion\Operator::EQ,
325
                        'Members'
326
                    ),
327
                    'sortClauses' => [new SortClause\ContentId()],
328
                ],
329
                $fixtureDir . 'Field.php',
330
            ],
331
            25 => [
332
                [
333
                    'filter' => new Criterion\Field(
334
                        'name',
335
                        Criterion\Operator::IN,
336
                        ['Members', 'Anonymous Users']
337
                    ),
338
                    'sortClauses' => [new SortClause\ContentId()],
339
                ],
340
                $fixtureDir . 'FieldIn.php',
341
            ],
342
            26 => [
343
                [
344
                    'filter' => new Criterion\DateMetadata(
345
                        Criterion\DateMetadata::MODIFIED,
346
                        Criterion\Operator::BETWEEN,
347
                        [1033920275, 1033920794]
348
                    ),
349
                    'sortClauses' => [new SortClause\ContentId()],
350
                ],
351
                $fixtureDir . 'FieldBetween.php',
352
            ],
353
            27 => [
354
                [
355
                    'filter' => new Criterion\LogicalOr(
356
                        [
357
                            new Criterion\Field(
358
                                'name',
359
                                Criterion\Operator::EQ,
360
                                'Members'
361
                            ),
362
                            new Criterion\DateMetadata(
363
                                Criterion\DateMetadata::MODIFIED,
364
                                Criterion\Operator::BETWEEN,
365
                                [1033920275, 1033920794]
366
                            ),
367
                        ]
368
                    ),
369
                    'sortClauses' => [new SortClause\ContentId()],
370
                ],
371
                $fixtureDir . 'FieldOr.php',
372
            ],
373
            28 => [
374
                [
375
                    'filter' => new Criterion\Subtree(
376
                        '/1/5/'
377
                    ),
378
                    'sortClauses' => [new SortClause\ContentId()],
379
                ],
380
                $fixtureDir . 'Subtree.php',
381
            ],
382
            29 => [
383
                [
384
                    'filter' => new Criterion\LocationId(
385
                        [1, 2, 5]
386
                    ),
387
                    'sortClauses' => [new SortClause\ContentId()],
388
                ],
389
                $fixtureDir . 'LocationId.php',
390
            ],
391
            30 => [
392
                [
393
                    'filter' => new Criterion\ParentLocationId(
394
                        [1]
395
                    ),
396
                    'sortClauses' => [new SortClause\ContentId()],
397
                ],
398
                $fixtureDir . 'ParentLocationId.php',
399
            ],
400
            31 => [
401
                [
402
                    'filter' => new Criterion\LocationRemoteId(
403
                        ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983']
404
                    ),
405
                    'sortClauses' => [new SortClause\ContentId()],
406
                ],
407
                $fixtureDir . 'LocationRemoteId.php',
408
            ],
409
            32 => [
410
                [
411
                    // There is no Status Criterion anymore, this should match all published as well
412
                    'filter' => new Criterion\Subtree(
413
                        '/1/'
414
                    ),
415
                    'sortClauses' => [new SortClause\ContentId()],
416
                    'limit' => 50,
417
                ],
418
                $fixtureDir . 'Status.php',
419
                // Result having the same sort level should be sorted between them to be system independent
420
                function (&$data) {
421
                    usort(
422
                        $data->searchHits,
423
                        function ($a, $b) {
424
                            if ($a->score == $b->score) {
425
                                if ($a->valueObject['id'] == $b->valueObject['id']) {
426
                                    return 0;
427
                                }
428
429
                                // Order by ascending ID
430
                                return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
431
                            }
432
433
                            // Order by descending score
434
                            return ($a->score > $b->score) ? -1 : 1;
435
                        }
436
                    );
437
                },
438
            ],
439
            33 => [
440
                [
441
                    'filter' => new Criterion\UserMetadata(
442
                        Criterion\UserMetadata::MODIFIER,
443
                        Criterion\Operator::EQ,
444
                        14
445
                    ),
446
                    'sortClauses' => [
447
                        new SortClause\ContentId(),
448
                    ],
449
                    'limit' => 50,
450
                ],
451
                $fixtureDir . 'UserMetadata.php',
452
            ],
453
            34 => [
454
                [
455
                    'filter' => new Criterion\UserMetadata(
456
                        Criterion\UserMetadata::MODIFIER,
457
                        Criterion\Operator::IN,
458
                        [14]
459
                    ),
460
                    'sortClauses' => [
461
                        new SortClause\ContentId(),
462
                    ],
463
                    'limit' => 50,
464
                ],
465
                $fixtureDir . 'UserMetadata.php',
466
            ],
467
            35 => [
468
                [
469
                    'filter' => new Criterion\UserMetadata(
470
                        Criterion\UserMetadata::OWNER,
471
                        Criterion\Operator::EQ,
472
                        14
473
                    ),
474
                    'sortClauses' => [
475
                        new SortClause\ContentId(),
476
                    ],
477
                    'limit' => 50,
478
                ],
479
                $fixtureDir . 'UserMetadata.php',
480
            ],
481
            36 => [
482
                [
483
                    'filter' => new Criterion\UserMetadata(
484
                        Criterion\UserMetadata::OWNER,
485
                        Criterion\Operator::IN,
486
                        [14]
487
                    ),
488
                    'sortClauses' => [
489
                        new SortClause\ContentId(),
490
                    ],
491
                    'limit' => 50,
492
                ],
493
                $fixtureDir . 'UserMetadata.php',
494
            ],
495
            37 => [
496
                [
497
                    'filter' => new Criterion\UserMetadata(
498
                        Criterion\UserMetadata::GROUP,
499
                        Criterion\Operator::EQ,
500
                        12
501
                    ),
502
                    'sortClauses' => [
503
                        new SortClause\ContentId(),
504
                    ],
505
                    'limit' => 50,
506
                ],
507
                $fixtureDir . 'UserMetadata.php',
508
            ],
509
            38 => [
510
                [
511
                    'filter' => new Criterion\UserMetadata(
512
                        Criterion\UserMetadata::GROUP,
513
                        Criterion\Operator::IN,
514
                        [12]
515
                    ),
516
                    'sortClauses' => [
517
                        new SortClause\ContentId(),
518
                    ],
519
                    'limit' => 50,
520
                ],
521
                $fixtureDir . 'UserMetadata.php',
522
            ],
523
            39 => [
524
                [
525
                    'filter' => new Criterion\UserMetadata(
526
                        Criterion\UserMetadata::GROUP,
527
                        Criterion\Operator::EQ,
528
                        4
529
                    ),
530
                    'sortClauses' => [
531
                        new SortClause\ContentId(),
532
                    ],
533
                    'limit' => 50,
534
                ],
535
                $fixtureDir . 'UserMetadata.php',
536
            ],
537
            40 => [
538
                [
539
                    'filter' => new Criterion\UserMetadata(
540
                        Criterion\UserMetadata::GROUP,
541
                        Criterion\Operator::IN,
542
                        [4]
543
                    ),
544
                    'sortClauses' => [
545
                        new SortClause\ContentId(),
546
                    ],
547
                    'limit' => 50,
548
                ],
549
                $fixtureDir . 'UserMetadata.php',
550
            ],
551
            41 => [
552
                [
553
                    'filter' => new Criterion\Ancestor(
554
                        [
555
                            '/1/5/44/',
556
                            '/1/5/44/45/',
557
                        ]
558
                    ),
559
                    'sortClauses' => [
560
                        new SortClause\ContentId(),
561
                    ],
562
                    'limit' => 50,
563
                ],
564
                $fixtureDir . 'AncestorContent.php',
565
            ],
566
        ];
567
    }
568
569
    public function getContentQuerySearches()
570
    {
571
        $fixtureDir = $this->getFixtureDir();
572
573
        return [
574
            [
575
                [
576
                    'filter' => new Criterion\ContentId(
577
                        [58, 10]
578
                    ),
579
                    'query' => new Criterion\FullText('contact'),
580
                    'sortClauses' => [new SortClause\ContentId()],
581
                ],
582
                $fixtureDir . 'FullTextFiltered.php',
583
            ],
584
            [
585
                [
586
                    'query' => new Criterion\FullText(
587
                        'contact',
588
                        [
589
                            'boost' => [
590
                                'title' => 2,
591
                            ],
592
                            'fuzziness' => .5,
593
                        ]
594
                    ),
595
                    'sortClauses' => [new SortClause\ContentId()],
596
                ],
597
                $fixtureDir . 'FullText.php',
598
            ],
599
            [
600
                [
601
                    'query' => new Criterion\FullText(
602
                        'Contact*'
603
                    ),
604
                    'sortClauses' => [new SortClause\ContentId()],
605
                ],
606
                $fixtureDir . 'FullTextWildcard.php',
607
            ],
608
            [
609
                [
610
                    'query' => new Criterion\LanguageCode('eng-GB', false),
611
                    'sortClauses' => [new SortClause\ContentId()],
612
                ],
613
                $fixtureDir . 'LanguageCode.php',
614
            ],
615
            [
616
                [
617
                    'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']),
618
                    'offset' => 10,
619
                    'sortClauses' => [new SortClause\ContentId()],
620
                ],
621
                $fixtureDir . 'LanguageCodeIn.php',
622
            ],
623
            [
624
                [
625
                    'query' => new Criterion\LanguageCode('eng-GB'),
626
                    'offset' => 10,
627
                    'sortClauses' => [new SortClause\ContentId()],
628
                ],
629
                $fixtureDir . 'LanguageCodeAlwaysAvailable.php',
630
            ],
631
            [
632
                [
633
                    'query' => new Criterion\Visibility(
634
                        Criterion\Visibility::VISIBLE
635
                    ),
636
                    'sortClauses' => [new SortClause\ContentId()],
637
                    'limit' => 50,
638
                ],
639
                $fixtureDir . 'Visibility.php',
640
            ],
641
        ];
642
    }
643
644
    public function getLocationQuerySearches()
645
    {
646
        $fixtureDir = $this->getFixtureDir();
647
648
        return [
649
            [
650
                [
651
                    'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1),
652
                    'sortClauses' => [new SortClause\ContentId()],
653
                ],
654
                $fixtureDir . 'Depth.php',
655
            ],
656
            [
657
                [
658
                    'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]),
659
                    'sortClauses' => [new SortClause\ContentId()],
660
                ],
661
                $fixtureDir . 'DepthIn.php',
662
            ],
663
            [
664
                [
665
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2),
666
                    'sortClauses' => [new SortClause\ContentId()],
667
                ],
668
                $fixtureDir . 'DepthGt.php',
669
            ],
670
            [
671
                [
672
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2),
673
                    'sortClauses' => [new SortClause\ContentId()],
674
                    'limit' => 50,
675
                ],
676
                $fixtureDir . 'DepthGte.php',
677
            ],
678
            [
679
                [
680
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2),
681
                    'sortClauses' => [new SortClause\ContentId()],
682
                ],
683
                $fixtureDir . 'Depth.php',
684
            ],
685
            [
686
                [
687
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2),
688
                    'sortClauses' => [new SortClause\ContentId()],
689
                    'limit' => 50,
690
                ],
691
                $fixtureDir . 'DepthLte.php',
692
            ],
693
            [
694
                [
695
                    'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]),
696
                    'sortClauses' => [new SortClause\ContentId()],
697
                    'limit' => 50,
698
                ],
699
                $fixtureDir . 'DepthLte.php',
700
            ],
701
            [
702
                [
703
                    'filter' => new Criterion\Ancestor('/1/5/44/45/'),
704
                    'sortClauses' => [
705
                        new SortClause\Location\Depth(),
706
                    ],
707
                    'limit' => 50,
708
                ],
709
                $fixtureDir . 'AncestorLocation.php',
710
            ],
711
        ];
712
    }
713
714
    /**
715
     * Test for the findContent() method.
716
     *
717
     * @dataProvider getFilterContentSearches
718
     *
719
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
720
     */
721
    public function testFindContentFiltered($queryData, $fixture, $closure = null)
722
    {
723
        $query = new Query($queryData);
724
        $this->assertQueryFixture($query, $fixture, $closure);
725
    }
726
727
    /**
728
     * Test for the findContentInfo() method.
729
     *
730
     * @dataProvider getFilterContentSearches
731
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
732
     */
733
    public function testFindContentInfoFiltered($queryData, $fixture, $closure = null)
734
    {
735
        $query = new Query($queryData);
736
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
737
    }
738
739
    /**
740
     * Test for the findLocations() method.
741
     *
742
     * @dataProvider getFilterContentSearches
743
     *
744
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
745
     */
746
    public function testFindLocationsContentFiltered($queryData, $fixture, $closure = null)
747
    {
748
        $query = new LocationQuery($queryData);
749
        $this->assertQueryFixture($query, $fixture, $closure);
750
    }
751
752
    /**
753
     * Test for deprecated $criterion property on query object.
754
     *
755
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
756
     * @deprecated
757
     */
758
    public function testDeprecatedCriteriaProperty()
759
    {
760
        $this->assertQueryFixture(
761
            new Query(
762
                [
763
                    'query' => new Criterion\ContentId(
764
                        [1, 4, 10]
765
                    ),
766
                    'sortClauses' => [new SortClause\ContentId()],
767
                ]
768
            ),
769
            $this->getFixtureDir() . 'DeprecatedContentIdQuery.php'
770
        );
771
    }
772
773
    /**
774
     * Test for the findContent() method.
775
     *
776
     * @dataProvider getContentQuerySearches
777
     *
778
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
779
     */
780
    public function testQueryContent($queryData, $fixture, $closure = null)
781
    {
782
        $query = new Query($queryData);
783
        $this->assertQueryFixture($query, $fixture, $closure);
784
    }
785
786
    /**
787
     * Test for the findContentInfo() method.
788
     *
789
     * @dataProvider getContentQuerySearches
790
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
791
     */
792
    public function testQueryContentInfo($queryData, $fixture, $closure = null)
793
    {
794
        $query = new Query($queryData);
795
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
796
    }
797
798
    /**
799
     * Test for the findLocations() method.
800
     *
801
     * @dataProvider getContentQuerySearches
802
     *
803
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
804
     */
805
    public function testQueryContentLocations($queryData, $fixture, $closure = null)
806
    {
807
        $query = new LocationQuery($queryData);
808
        $this->assertQueryFixture($query, $fixture, $closure);
809
    }
810
811
    /**
812
     * Test for the findLocations() method.
813
     *
814
     * @dataProvider getLocationQuerySearches
815
     *
816
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
817
     */
818
    public function testQueryLocations($queryData, $fixture, $closure = null)
819
    {
820
        $query = new LocationQuery($queryData);
821
        $this->assertQueryFixture($query, $fixture, $closure);
822
    }
823
824
    public function getCaseInsensitiveSearches()
825
    {
826
        return [
827
            [
828
                [
829
                    'filter' => new Criterion\Field(
830
                        'name',
831
                        Criterion\Operator::EQ,
832
                        'Members'
833
                    ),
834
                    'sortClauses' => [new SortClause\ContentId()],
835
                ],
836
            ],
837
            [
838
                [
839
                    'filter' => new Criterion\Field(
840
                        'name',
841
                        Criterion\Operator::EQ,
842
                        'members'
843
                    ),
844
                    'sortClauses' => [new SortClause\ContentId()],
845
                ],
846
            ],
847
            [
848
                [
849
                    'filter' => new Criterion\Field(
850
                        'name',
851
                        Criterion\Operator::EQ,
852
                        'MEMBERS'
853
                    ),
854
                    'sortClauses' => [new SortClause\ContentId()],
855
                ],
856
            ],
857
        ];
858
    }
859
860
    /**
861
     * Test for the findContent() method.
862
     *
863
     * @dataProvider getCaseInsensitiveSearches
864
     *
865
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
866
     */
867
    public function testFindContentFieldFiltersCaseSensitivity($queryData)
868
    {
869
        $query = new Query($queryData);
870
        $this->assertQueryFixture(
871
            $query,
872
            $this->getFixtureDir() . 'Field.php'
873
        );
874
    }
875
876
    /**
877
     * Test for the findLocations() method.
878
     *
879
     * @dataProvider getCaseInsensitiveSearches
880
     *
881
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
882
     */
883
    public function testFindLocationsFieldFiltersCaseSensitivity($queryData)
884
    {
885
        $query = new LocationQuery($queryData);
886
        $this->assertQueryFixture(
887
            $query,
888
            $this->getFixtureDir() . 'Field.php'
889
        );
890
    }
891
892
    public function getRelationFieldFilterSearches()
893
    {
894
        $fixtureDir = $this->getFixtureDir();
895
896
        return [
897
            0 => [
898
                [
899
                    'filter' => new Criterion\FieldRelation(
900
                        'image',
901
                        Criterion\Operator::IN,
902
                        [1, 4, 10]
903
                    ),
904
                    'sortClauses' => [new SortClause\ContentId()],
905
                ],
906
                $fixtureDir . 'FieldRelation.php',
907
            ],
908
            1 => [
909
                [
910
                    'filter' => new Criterion\FieldRelation(
911
                        'image',
912
                        Criterion\Operator::IN,
913
                        [4, 49]
914
                    ),
915
                    'sortClauses' => [new SortClause\ContentId()],
916
                ],
917
                $fixtureDir . 'FieldRelationAll.php',
918
            ],
919
            2 => [
920
                [
921
                    'filter' => new Criterion\FieldRelation(
922
                        'image',
923
                        Criterion\Operator::IN,
924
                        [4]
925
                    ),
926
                    'sortClauses' => [new SortClause\ContentId()],
927
                ],
928
                $fixtureDir . 'FieldRelation.php',
929
            ],
930
            3 => [
931
                [
932
                    'filter' => new Criterion\FieldRelation(
933
                        'image',
934
                        Criterion\Operator::CONTAINS,
935
                        [1, 4, 10]
936
                    ),
937
                    'sortClauses' => [new SortClause\ContentId()],
938
                ],
939
                $fixtureDir . 'MatchNone.php',
940
            ],
941
            4 => [
942
                [
943
                    'filter' => new Criterion\FieldRelation(
944
                        'image',
945
                        Criterion\Operator::CONTAINS,
946
                        [4, 49]
947
                    ),
948
                    'sortClauses' => [new SortClause\ContentId()],
949
                ],
950
                $fixtureDir . 'MatchNone.php',
951
            ],
952
            5 => [
953
                [
954
                    'filter' => new Criterion\FieldRelation(
955
                        'image',
956
                        Criterion\Operator::CONTAINS,
957
                        [4]
958
                    ),
959
                    'sortClauses' => [new SortClause\ContentId()],
960
                ],
961
                $fixtureDir . 'FieldRelation.php',
962
            ],
963
        ];
964
    }
965
966
    /**
967
     * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered()
968
     * and testFindRelationFieldLocationsFiltered().
969
     */
970
    public function testRelationContentCreation()
971
    {
972
        $repository = $this->getRepository();
973
        $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery');
974
        $contentService = $repository->getContentService();
975
        $locationService = $repository->getLocationService();
976
977
        $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home
978
979
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
980
        $createStruct->setField('name', 'Image gallery');
981
        $createStruct->setField('image', 49); // Images folder
982
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
983
        $contentService->publishVersion($draft->getVersionInfo());
984
985
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
986
        $createStruct->setField('name', 'User gallery');
987
        $createStruct->setField('image', 4); // User folder
988
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
989
        $contentService->publishVersion($draft->getVersionInfo());
990
991
        $this->refreshSearch($repository);
992
    }
993
994
    /**
995
     * Test for FieldRelation using findContentInfo() method.
996
     *
997
     * @dataProvider getRelationFieldFilterSearches
998
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
999
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1000
     */
1001
    public function testFindRelationFieldContentInfoFiltered($queryData, $fixture)
1002
    {
1003
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1004
        $query = new Query($queryData);
1005
        $this->assertQueryFixture($query, $fixture, null, true, true, false);
1006
    }
1007
1008
    /**
1009
     * Test for FieldRelation using findLocations() method.
1010
     *
1011
     * @dataProvider getRelationFieldFilterSearches
1012
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
1013
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1014
     */
1015
    public function testFindRelationFieldLocationsFiltered($queryData, $fixture)
1016
    {
1017
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1018
        $query = new LocationQuery($queryData);
1019
        $this->assertQueryFixture($query, $fixture, null, true, false, false);
1020
    }
1021
1022 View Code Duplication
    public function testFindSingle()
1023
    {
1024
        $repository = $this->getRepository();
1025
        $searchService = $repository->getSearchService();
1026
1027
        $content = $searchService->findSingle(
1028
            new Criterion\ContentId(
1029
                [4]
1030
            )
1031
        );
1032
1033
        $this->assertEquals(
1034
            4,
1035
            $content->id
1036
        );
1037
    }
1038
1039 View Code Duplication
    public function testFindNoPerformCount()
1040
    {
1041
        $repository = $this->getRepository();
1042
        $searchService = $repository->getSearchService();
1043
1044
        $query = new Query();
1045
        $query->performCount = false;
1046
        $query->query = new Criterion\ContentTypeId(
1047
            [4]
1048
        );
1049
1050
        $searchHit = $searchService->findContent($query);
1051
1052
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1053
            $this->assertNull(
1054
                $searchHit->totalCount
1055
            );
1056
        } else {
1057
            $this->assertEquals(
1058
                2,
1059
                $searchHit->totalCount
1060
            );
1061
        }
1062
    }
1063
1064
    /**
1065
     * @expectedException \RuntimeException
1066
     */
1067 View Code Duplication
    public function testFindNoPerformCountException()
1068
    {
1069
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1070
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1071
        }
1072
1073
        $repository = $this->getRepository();
1074
        $searchService = $repository->getSearchService();
1075
1076
        $query = new Query();
1077
        $query->performCount = false;
1078
        $query->limit = 0;
1079
        $query->query = new Criterion\ContentTypeId(
1080
            [4]
1081
        );
1082
1083
        $searchService->findContent($query);
1084
    }
1085
1086 View Code Duplication
    public function testFindLocationsNoPerformCount()
1087
    {
1088
        $repository = $this->getRepository();
1089
        $searchService = $repository->getSearchService();
1090
1091
        $query = new LocationQuery();
1092
        $query->performCount = false;
1093
        $query->query = new Criterion\ContentTypeId(
1094
            [4]
1095
        );
1096
1097
        $searchHit = $searchService->findLocations($query);
1098
1099
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1100
            $this->assertNull(
1101
                $searchHit->totalCount
1102
            );
1103
        } else {
1104
            $this->assertEquals(
1105
                2,
1106
                $searchHit->totalCount
1107
            );
1108
        }
1109
    }
1110
1111
    /**
1112
     * @expectedException \RuntimeException
1113
     */
1114 View Code Duplication
    public function testFindLocationsNoPerformCountException()
1115
    {
1116
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1117
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1118
        }
1119
1120
        $repository = $this->getRepository();
1121
        $searchService = $repository->getSearchService();
1122
1123
        $query = new LocationQuery();
1124
        $query->performCount = false;
1125
        $query->limit = 0;
1126
        $query->query = new Criterion\ContentTypeId(
1127
            [4]
1128
        );
1129
1130
        $searchService->findLocations($query);
1131
    }
1132
1133
    /**
1134
     * Create test Content with ezcountry field having multiple countries selected.
1135
     *
1136
     * @return Content
1137
     */
1138
    protected function createMultipleCountriesContent()
1139
    {
1140
        $repository = $this->getRepository();
1141
        $contentTypeService = $repository->getContentTypeService();
1142
        $contentService = $repository->getContentService();
1143
1144
        $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple');
1145
        $createStruct->mainLanguageCode = 'eng-GB';
1146
        $createStruct->remoteId = 'countries-multiple-123';
1147
        $createStruct->names = ['eng-GB' => 'Multiple countries'];
1148
        $createStruct->creatorId = 14;
1149
        $createStruct->creationDate = new \DateTime();
1150
1151
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry');
1152
        $fieldCreate->names = ['eng-GB' => 'Countries'];
1153
        $fieldCreate->fieldGroup = 'main';
1154
        $fieldCreate->position = 1;
1155
        $fieldCreate->isTranslatable = false;
1156
        $fieldCreate->isSearchable = true;
1157
        $fieldCreate->fieldSettings = ['isMultiple' => true];
1158
1159
        $createStruct->addFieldDefinition($fieldCreate);
1160
1161
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1162
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1163
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1164
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1165
1166
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1167
        $createStruct->remoteId = 'countries-multiple-456';
1168
        $createStruct->alwaysAvailable = false;
1169
        $createStruct->setField(
1170
            'countries',
1171
            ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU']
1172
        );
1173
1174
        $draft = $contentService->createContent($createStruct);
1175
        $content = $contentService->publishVersion($draft->getVersionInfo());
1176
1177
        $this->refreshSearch($repository);
1178
1179
        return $content;
1180
    }
1181
1182
    /**
1183
     * Test for the findContent() method.
1184
     *
1185
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1186
     */
1187 View Code Duplication
    public function testFieldCollectionContains()
1188
    {
1189
        $testContent = $this->createMultipleCountriesContent();
1190
1191
        $query = new Query(
1192
            [
1193
                'query' => new Criterion\Field(
1194
                    'countries',
1195
                    Criterion\Operator::CONTAINS,
1196
                    'Belgium'
1197
                ),
1198
            ]
1199
        );
1200
1201
        $repository = $this->getRepository();
1202
        $searchService = $repository->getSearchService();
1203
        $result = $searchService->findContent($query);
1204
1205
        $this->assertEquals(1, $result->totalCount);
1206
        $this->assertEquals(
1207
            $testContent->id,
1208
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1209
        );
1210
    }
1211
1212
    /**
1213
     * Test for the findContent() method.
1214
     *
1215
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1216
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains
1217
     */
1218 View Code Duplication
    public function testFieldCollectionContainsNoMatch()
1219
    {
1220
        $this->createMultipleCountriesContent();
1221
        $query = new Query(
1222
            [
1223
                'query' => new Criterion\Field(
1224
                    'countries',
1225
                    Criterion\Operator::CONTAINS,
1226
                    'Netherlands Antilles'
1227
                ),
1228
            ]
1229
        );
1230
1231
        $repository = $this->getRepository();
1232
        $searchService = $repository->getSearchService();
1233
        $result = $searchService->findContent($query);
1234
1235
        $this->assertEquals(0, $result->totalCount);
1236
    }
1237
1238
    /**
1239
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1240
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1241
     */
1242 View Code Duplication
    public function testInvalidFieldIdentifierRange()
1243
    {
1244
        $repository = $this->getRepository();
1245
        $searchService = $repository->getSearchService();
1246
1247
        $searchService->findContent(
1248
            new Query(
1249
                [
1250
                    'filter' => new Criterion\Field(
1251
                        'some_hopefully_unknown_field',
1252
                        Criterion\Operator::BETWEEN,
1253
                        [10, 1000]
1254
                    ),
1255
                    'sortClauses' => [new SortClause\ContentId()],
1256
                ]
1257
            )
1258
        );
1259
    }
1260
1261
    /**
1262
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1263
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1264
     */
1265 View Code Duplication
    public function testInvalidFieldIdentifierIn()
1266
    {
1267
        $repository = $this->getRepository();
1268
        $searchService = $repository->getSearchService();
1269
1270
        $searchService->findContent(
1271
            new Query(
1272
                [
1273
                    'filter' => new Criterion\Field(
1274
                        'some_hopefully_unknown_field',
1275
                        Criterion\Operator::EQ,
1276
                        1000
1277
                    ),
1278
                    'sortClauses' => [new SortClause\ContentId()],
1279
                ]
1280
            )
1281
        );
1282
    }
1283
1284
    /**
1285
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1286
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'tag_cloud_url'
1287
     */
1288 View Code Duplication
    public function testFindContentWithNonSearchableField()
1289
    {
1290
        $repository = $this->getRepository();
1291
        $searchService = $repository->getSearchService();
1292
1293
        $searchService->findContent(
1294
            new Query(
1295
                [
1296
                    'filter' => new Criterion\Field(
1297
                        'tag_cloud_url',
1298
                        Criterion\Operator::EQ,
1299
                        'http://nimbus.com'
1300
                    ),
1301
                    'sortClauses' => [new SortClause\ContentId()],
1302
                ]
1303
            )
1304
        );
1305
    }
1306
1307
    /**
1308
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1309
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1310
     */
1311 View Code Duplication
    public function testSortFieldWithNonSearchableField()
1312
    {
1313
        $repository = $this->getRepository();
1314
        $searchService = $repository->getSearchService();
1315
1316
        $searchService->findContent(
1317
            new Query(
1318
                [
1319
                    'sortClauses' => [new SortClause\Field('template_look', 'title')],
1320
                ]
1321
            )
1322
        );
1323
    }
1324
1325
    /**
1326
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1327
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1328
     */
1329 View Code Duplication
    public function testSortMapLocationDistanceWithNonSearchableField()
1330
    {
1331
        $repository = $this->getRepository();
1332
        $searchService = $repository->getSearchService();
1333
1334
        $searchService->findContent(
1335
            new Query(
1336
                [
1337
                    'sortClauses' => [
1338
                        new SortClause\MapLocationDistance(
1339
                            'template_look',
1340
                            'title',
1341
                            1,
1342
                            2
1343
                        ),
1344
                    ],
1345
                ]
1346
            )
1347
        );
1348
    }
1349
1350
    /**
1351
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1352
     */
1353 View Code Duplication
    public function testFindSingleFailMultiple()
1354
    {
1355
        $repository = $this->getRepository();
1356
        $searchService = $repository->getSearchService();
1357
1358
        $searchService->findSingle(
1359
            new Criterion\ContentId(
1360
                [4, 10]
1361
            )
1362
        );
1363
    }
1364
1365
    /**
1366
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1367
     */
1368
    public function testFindSingleWithNonSearchableField()
1369
    {
1370
        $repository = $this->getRepository();
1371
        $searchService = $repository->getSearchService();
1372
1373
        $searchService->findSingle(
1374
            new Criterion\Field(
1375
                'tag_cloud_url',
1376
                Criterion\Operator::EQ,
1377
                'http://nimbus.com'
1378
            )
1379
        );
1380
    }
1381
1382
    public function getSortedContentSearches()
1383
    {
1384
        $fixtureDir = $this->getFixtureDir();
1385
1386
        return [
1387
            0 => [
1388
                [
1389
                    'filter' => new Criterion\SectionId([2]),
1390
                    'offset' => 0,
1391
                    'limit' => 10,
1392
                    'sortClauses' => [],
1393
                ],
1394
                $fixtureDir . 'SortNone.php',
1395
                // Result having the same sort level should be sorted between them to be system independent
1396
                function (&$data) {
1397
                    usort(
1398
                        $data->searchHits,
1399
                        function ($a, $b) {
1400
                            return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
1401
                        }
1402
                    );
1403
                },
1404
            ],
1405
            1 => [
1406
                [
1407
                    'filter' => new Criterion\SectionId([2]),
1408
                    'offset' => 0,
1409
                    'limit' => 10,
1410
                    'sortClauses' => [
1411
                        new SortClause\DatePublished(),
1412
                        new SortClause\ContentId(),
1413
                    ],
1414
                ],
1415
                $fixtureDir . 'SortDatePublished.php',
1416
            ],
1417
            2 => [
1418
                [
1419
                    'filter' => new Criterion\SectionId([2]),
1420
                    'offset' => 0,
1421
                    'limit' => 50,
1422
                    'sortClauses' => [
1423
                        new SortClause\DateModified(),
1424
                        new SortClause\ContentId(),
1425
                    ],
1426
                ],
1427
                $fixtureDir . 'SortDateModified.php',
1428
            ],
1429
            3 => [
1430
                [
1431
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1432
                    'offset' => 0,
1433
                    'limit' => 50,
1434
                    'sortClauses' => [
1435
                        new SortClause\SectionIdentifier(),
1436
                        new SortClause\ContentId(),
1437
                    ],
1438
                ],
1439
                $fixtureDir . 'SortSectionIdentifier.php',
1440
            ],
1441
            4 => [
1442
                [
1443
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1444
                    'offset' => 0,
1445
                    'limit' => 50,
1446
                    'sortClauses' => [
1447
                        new SortClause\SectionName(),
1448
                        new SortClause\ContentId(),
1449
                    ],
1450
                ],
1451
                $fixtureDir . 'SortSectionName.php',
1452
            ],
1453
            5 => [
1454
                [
1455
                    'filter' => new Criterion\SectionId([2, 3]),
1456
                    'offset' => 0,
1457
                    'limit' => 50,
1458
                    'sortClauses' => [
1459
                        new SortClause\ContentName(),
1460
                        new SortClause\ContentId(),
1461
                    ],
1462
                ],
1463
                $fixtureDir . 'SortContentName.php',
1464
            ],
1465
            6 => [
1466
                [
1467
                    'filter' => new Criterion\ContentTypeId(1),
1468
                    'offset' => 0,
1469
                    'limit' => 50,
1470
                    'sortClauses' => [
1471
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1472
                        new SortClause\ContentId(),
1473
                    ],
1474
                ],
1475
                $fixtureDir . 'SortFolderName.php',
1476
            ],
1477
            7 => [
1478
                [
1479
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1480
                    'offset' => 0,
1481
                    'limit' => 50,
1482
                    'sortClauses' => [
1483
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1484
                        new SortClause\ContentId(),
1485
                    ],
1486
                ],
1487
                $fixtureDir . 'SortFieldMultipleTypes.php',
1488
            ],
1489
            8 => [
1490
                [
1491
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1492
                    'offset' => 0,
1493
                    'limit' => 50,
1494
                    'sortClauses' => [
1495
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1496
                        new SortClause\ContentId(),
1497
                    ],
1498
                ],
1499
                $fixtureDir . 'SortFieldMultipleTypesReverse.php',
1500
            ],
1501
            9 => [
1502
                [
1503
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1504
                    'offset' => 3,
1505
                    'limit' => 5,
1506
                    'sortClauses' => [
1507
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1508
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1509
                        new SortClause\ContentId(),
1510
                    ],
1511
                ],
1512
                $fixtureDir . 'SortFieldMultipleTypesSlice.php',
1513
            ],
1514
            10 => [
1515
                [
1516
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1517
                    'offset' => 3,
1518
                    'limit' => 5,
1519
                    'sortClauses' => [
1520
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1521
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1522
                        new SortClause\ContentId(),
1523
                    ],
1524
                ],
1525
                $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php',
1526
            ],
1527
        ];
1528
    }
1529
1530
    public function getSortedLocationSearches()
1531
    {
1532
        $fixtureDir = $this->getFixtureDir();
1533
1534
        return [
1535
            [
1536
                [
1537
                    'filter' => new Criterion\SectionId([2]),
1538
                    'offset' => 0,
1539
                    'limit' => 10,
1540
                    'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)],
1541
                ],
1542
                $fixtureDir . 'SortPathString.php',
1543
            ],
1544
            [
1545
                [
1546
                    'filter' => new Criterion\SectionId([2]),
1547
                    'offset' => 0,
1548
                    'limit' => 10,
1549
                    'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)],
1550
                ],
1551
                $fixtureDir . 'SortLocationDepth.php',
1552
                // Result having the same sort level should be sorted between them to be system independent
1553
                function (&$data) {
1554
                    // Result with ids:
1555
                    //     4 has depth = 1
1556
                    //     11, 12, 13, 42, 59 have depth = 2
1557
                    //     10, 14 have depth = 3
1558
                    $map = [
1559
                        4 => 0,
1560
                        11 => 1,
1561
                        12 => 2,
1562
                        13 => 3,
1563
                        42 => 4,
1564
                        59 => 5,
1565
                        10 => 6,
1566
                        14 => 7,
1567
                    ];
1568
                    usort(
1569
                        $data->searchHits,
1570
                        function ($a, $b) use ($map) {
1571
                            return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1;
1572
                        }
1573
                    );
1574
                },
1575
            ],
1576
            [
1577
                [
1578
                    'filter' => new Criterion\SectionId([3]),
1579
                    'offset' => 0,
1580
                    'limit' => 10,
1581
                    'sortClauses' => [
1582
                        new SortClause\Location\Path(Query::SORT_DESC),
1583
                        new SortClause\ContentName(Query::SORT_ASC),
1584
                    ],
1585
                ],
1586
                $fixtureDir . 'SortMultiple.php',
1587
            ],
1588
            [
1589
                [
1590
                    'filter' => new Criterion\SectionId([2]),
1591
                    'offset' => 0,
1592
                    'limit' => 10,
1593
                    'sortClauses' => [
1594
                        new SortClause\Location\Priority(Query::SORT_DESC),
1595
                        new SortClause\ContentId(),
1596
                    ],
1597
                ],
1598
                $fixtureDir . 'SortDesc.php',
1599
            ],
1600
        ];
1601
    }
1602
1603
    /**
1604
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
1605
     */
1606 View Code Duplication
    protected function createTestContentType()
1607
    {
1608
        $repository = $this->getRepository();
1609
        $contentTypeService = $repository->getContentTypeService();
1610
1611
        $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type');
1612
        $createStruct->mainLanguageCode = 'eng-GB';
1613
        $createStruct->names = ['eng-GB' => 'Test type'];
1614
        $createStruct->creatorId = 14;
1615
        $createStruct->creationDate = new \DateTime();
1616
1617
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger');
1618
        $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field'];
1619
        $translatableFieldCreate->fieldGroup = 'main';
1620
        $translatableFieldCreate->position = 1;
1621
        $translatableFieldCreate->isTranslatable = true;
1622
        $translatableFieldCreate->isSearchable = true;
1623
1624
        $createStruct->addFieldDefinition($translatableFieldCreate);
1625
1626
        $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger');
1627
        $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field'];
1628
        $nonTranslatableFieldCreate->fieldGroup = 'main';
1629
        $nonTranslatableFieldCreate->position = 2;
1630
        $nonTranslatableFieldCreate->isTranslatable = false;
1631
        $nonTranslatableFieldCreate->isSearchable = true;
1632
1633
        $createStruct->addFieldDefinition($nonTranslatableFieldCreate);
1634
1635
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1636
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1637
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1638
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1639
1640
        return $contentType;
1641
    }
1642
1643
    /**
1644
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1645
     * @param int $fieldValue11 Value for translatable field in first language
1646
     * @param int $fieldValue12 Value for translatable field in second language
1647
     * @param int $fieldValue2 Value for non translatable field
1648
     * @param string $mainLanguageCode
1649
     * @param bool $alwaysAvailable
1650
     *
1651
     * @return Content
1652
     */
1653
    protected function createMultilingualContent(
1654
        $contentType,
1655
        $fieldValue11 = null,
1656
        $fieldValue12 = null,
1657
        $fieldValue2 = null,
1658
        $mainLanguageCode = 'eng-GB',
1659
        $alwaysAvailable = false
1660
    ) {
1661
        $repository = $this->getRepository();
1662
        $contentService = $repository->getContentService();
1663
1664
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1665
        $createStruct->alwaysAvailable = $alwaysAvailable;
1666
        $createStruct->mainLanguageCode = $mainLanguageCode;
1667
        if ($fieldValue11) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fieldValue11 of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1668
            $createStruct->setField('integer', $fieldValue11, 'eng-GB');
1669
        }
1670
        if ($fieldValue12) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fieldValue12 of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1671
            $createStruct->setField('integer', $fieldValue12, 'ger-DE');
1672
        }
1673
        $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode);
1674
1675
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
1676
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1677
        $content = $contentService->publishVersion($draft->getVersionInfo());
1678
1679
        $this->refreshSearch($repository);
1680
1681
        return $content;
1682
    }
1683
1684
    protected function checkPrioritizedLanguagesSupport()
1685
    {
1686
        $setupFactory = $this->getSetupFactory();
1687
        if ($setupFactory instanceof LegacyElasticsearch) {
1688
            $this->markTestIncomplete('Prioritized languages are not supported with Elasticsearch engine');
1689
        }
1690
    }
1691
1692
    public function providerForTestMultilingualFieldSort()
1693
    {
1694
        return [
1695
            0 => [
1696
                [
1697
                    1 => [1, 2, 1],
1698
                    2 => [2, 1, 2],
1699
                    3 => [2, 1, 3],
1700
                    4 => [1, 2, 4],
1701
                ],
1702
                [
1703
                    'languages' => [
1704
                        'eng-GB',
1705
                        'ger-DE',
1706
                    ],
1707
                ],
1708
                [
1709
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1710
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1711
                ],
1712
                /**
1713
                 * Expected order, Value eng-GB, Value ger-DE.
1714
                 *
1715
                 * Content 4, 1, 2, 4
1716
                 * Content 1, 1, 2, 1
1717
                 * Content 3, 2, 1, 3
1718
                 * Content 2, 2, 1, 2
1719
                 */
1720
                [4, 1, 3, 2],
1721
            ],
1722
            1 => [
1723
                [
1724
                    1 => [1, 2, 1],
1725
                    2 => [2, 1, 2],
1726
                    3 => [2, 1, 3],
1727
                    4 => [1, 2, 4],
1728
                ],
1729
                [
1730
                    'languages' => [
1731
                        'ger-DE',
1732
                        'eng-GB',
1733
                    ],
1734
                ],
1735
                [
1736
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1737
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1738
                ],
1739
                /**
1740
                 * Expected order, Value eng-GB, Value ger-DE.
1741
                 *
1742
                 * Content 3, 2, 1, 3
1743
                 * Content 2, 2, 1, 2
1744
                 * Content 4, 1, 2, 4
1745
                 * Content 1, 1, 2, 1
1746
                 */
1747
                [3, 2, 4, 1],
1748
            ],
1749
            2 => [
1750
                [
1751
                    1 => [null, 2, null, 'ger-DE'],
1752
                    2 => [3, null, null, 'eng-GB'],
1753
                    3 => [4, null, null, 'eng-GB'],
1754
                    4 => [null, 1, null, 'ger-DE'],
1755
                ],
1756
                [
1757
                    'languages' => [
1758
                        'eng-GB',
1759
                    ],
1760
                ],
1761
                [
1762
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1763
                ],
1764
                /**
1765
                 * Expected order, Value eng-GB, Value ger-DE.
1766
                 *
1767
                 * Content 3, 4, -
1768
                 * Content 2, 3, -
1769
                 */
1770
                [3, 2],
1771
            ],
1772
            3 => [
1773
                [
1774
                    1 => [null, 2, null, 'ger-DE'],
1775
                    2 => [3, null, null, 'eng-GB'],
1776
                    3 => [4, null, null, 'eng-GB'],
1777
                    4 => [null, 1, null, 'ger-DE'],
1778
                ],
1779
                [
1780
                    'languages' => [
1781
                        'ger-DE',
1782
                    ],
1783
                ],
1784
                [
1785
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1786
                ],
1787
                /**
1788
                 * Expected order, Value eng-GB, Value ger-DE.
1789
                 *
1790
                 * Content 1, -, 2
1791
                 * Content 4, -, 1
1792
                 */
1793
                [1, 4],
1794
            ],
1795
            4 => [
1796
                [
1797
                    1 => [null, 2, null, 'ger-DE'],
1798
                    2 => [3, null, null, 'eng-GB'],
1799
                    3 => [4, null, null, 'eng-GB'],
1800
                    4 => [null, 1, null, 'ger-DE'],
1801
                ],
1802
                [
1803
                    'languages' => [
1804
                        'eng-GB',
1805
                        'ger-DE',
1806
                    ],
1807
                ],
1808
                [
1809
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1810
                ],
1811
                /**
1812
                 * Expected order, Value eng-GB, Value ger-DE.
1813
                 *
1814
                 * Content 3, 4, -
1815
                 * Content 2, 3, -
1816
                 * Content 1, -, 2
1817
                 * Content 4, -, 1
1818
                 */
1819
                [3, 2, 1, 4],
1820
            ],
1821
            5 => [
1822
                [
1823
                    1 => [null, 2, null, 'ger-DE'],
1824
                    2 => [3, null, null, 'eng-GB'],
1825
                    3 => [4, null, null, 'eng-GB'],
1826
                    4 => [null, 1, null, 'ger-DE'],
1827
                ],
1828
                [
1829
                    'languages' => [
1830
                        'ger-DE',
1831
                        'eng-GB',
1832
                    ],
1833
                ],
1834
                [
1835
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1836
                ],
1837
                /**
1838
                 * Expected order, Value eng-GB, Value ger-DE.
1839
                 *
1840
                 * Content 3, 4, -
1841
                 * Content 2, 3, -
1842
                 * Content 1, -, 2
1843
                 * Content 4, -, 1
1844
                 */
1845
                [3, 2, 1, 4],
1846
            ],
1847
            6 => [
1848
                [
1849
                    1 => [null, 2, null, 'ger-DE'],
1850
                    2 => [3, 4, null, 'eng-GB'],
1851
                    3 => [4, 3, null, 'eng-GB'],
1852
                    4 => [null, 1, null, 'ger-DE'],
1853
                ],
1854
                [
1855
                    'languages' => [
1856
                        'eng-GB',
1857
                        'ger-DE',
1858
                    ],
1859
                ],
1860
                [
1861
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1862
                ],
1863
                /**
1864
                 * Expected order, Value eng-GB, Value ger-DE.
1865
                 *
1866
                 * Content 3, 4, 3
1867
                 * Content 2, 3, 4
1868
                 * Content 1, -, 2
1869
                 * Content 4, -, 1
1870
                 */
1871
                [3, 2, 1, 4],
1872
            ],
1873
            7 => [
1874
                [
1875
                    1 => [null, 2, null, 'ger-DE'],
1876
                    2 => [3, 4, null, 'eng-GB'],
1877
                    3 => [4, 3, null, 'eng-GB'],
1878
                    4 => [null, 1, null, 'ger-DE'],
1879
                ],
1880
                [
1881
                    'languages' => [
1882
                        'ger-DE',
1883
                        'eng-GB',
1884
                    ],
1885
                ],
1886
                [
1887
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1888
                ],
1889
                /**
1890
                 * Expected order, Value eng-GB, Value ger-DE.
1891
                 *
1892
                 * Content 2, 3, 4
1893
                 * Content 3, 4, 3
1894
                 * Content 1, -, 2
1895
                 * Content 4, -, 1
1896
                 */
1897
                [2, 3, 1, 4],
1898
            ],
1899
            8 => [
1900
                [
1901
                    1 => [null, 1, null, 'ger-DE', true],
1902
                    2 => [4, null, null, 'eng-GB', true],
1903
                    3 => [3, null, null, 'eng-GB', false],
1904
                    4 => [null, 2, null, 'ger-DE', false],
1905
                ],
1906
                [
1907
                    'languages' => [
1908
                        'eng-GB',
1909
                    ],
1910
                ],
1911
                [
1912
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1913
                ],
1914
                /**
1915
                 * Expected order, Value eng-GB, Value ger-DE.
1916
                 *
1917
                 * Content 1, -, 1
1918
                 * Content 3, 3, -
1919
                 * Content 2, 4, -
1920
                 */
1921
                [1, 3, 2],
1922
            ],
1923
            9 => [
1924
                [
1925
                    1 => [null, 1, null, 'ger-DE', true],
1926
                    2 => [4, null, null, 'eng-GB', true],
1927
                    3 => [3, null, null, 'eng-GB', false],
1928
                    4 => [null, 2, null, 'ger-DE', false],
1929
                ],
1930
                [
1931
                    'languages' => [
1932
                        'ger-DE',
1933
                    ],
1934
                ],
1935
                [
1936
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1937
                ],
1938
                /**
1939
                 * Expected order, Value eng-GB, Value ger-DE.
1940
                 *
1941
                 * Content 2, 4, -
1942
                 * Content 4, -, 2
1943
                 * Content 1, -, 1
1944
                 */
1945
                [2, 4, 1],
1946
            ],
1947
            10 => [
1948
                [
1949
                    1 => [null, 1, null, 'ger-DE', true],
1950
                    2 => [4, null, null, 'eng-GB', true],
1951
                    3 => [3, null, null, 'eng-GB', false],
1952
                    4 => [null, 2, null, 'ger-DE', false],
1953
                ],
1954
                [
1955
                    'languages' => [
1956
                        'eng-GB',
1957
                    ],
1958
                    'useAlwaysAvailable' => false,
1959
                ],
1960
                [
1961
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1962
                ],
1963
                /**
1964
                 * Expected order, Value eng-GB, Value ger-DE.
1965
                 *
1966
                 * Content 3, 3, -
1967
                 * Content 2, 4, -
1968
                 */
1969
                [3, 2],
1970
            ],
1971
            11 => [
1972
                [
1973
                    1 => [null, 1, null, 'ger-DE', true],
1974
                    2 => [4, null, null, 'eng-GB', true],
1975
                    3 => [3, null, null, 'eng-GB', false],
1976
                    4 => [null, 2, null, 'ger-DE', false],
1977
                ],
1978
                [
1979
                    'languages' => [
1980
                        'ger-DE',
1981
                    ],
1982
                    'useAlwaysAvailable' => false,
1983
                ],
1984
                [
1985
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1986
                ],
1987
                /**
1988
                 * Expected order, Value eng-GB, Value ger-DE.
1989
                 *
1990
                 * Content 4, -, 2
1991
                 * Content 1, -, 1
1992
                 */
1993
                [4, 1],
1994
            ],
1995
        ];
1996
    }
1997
1998
    /**
1999
     * Test for the findContent() method.
2000
     *
2001
     * @group rrr
2002
     * @dataProvider providerForTestMultilingualFieldSort
2003
     *
2004
     * @param array $contentDataList
2005
     * @param array $languageSettings
2006
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2007
     * @param array $expected
2008
     */
2009
    public function testMultilingualFieldSortContent(
2010
        array $contentDataList,
2011
        $languageSettings,
2012
        array $sortClauses,
2013
        $expected
2014
    ) {
2015
        $this->assertMultilingualFieldSort(
2016
            $contentDataList,
2017
            $languageSettings,
2018
            $sortClauses,
2019
            $expected
2020
        );
2021
    }
2022
2023
    /**
2024
     * Test for the findLocations() method.
2025
     *
2026
     * @group rrr
2027
     * @dataProvider providerForTestMultilingualFieldSort
2028
     *
2029
     * @param array $contentDataList
2030
     * @param array $languageSettings
2031
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2032
     * @param array $expected
2033
     */
2034
    public function testMultilingualFieldSortLocation(
2035
        array $contentDataList,
2036
        $languageSettings,
2037
        array $sortClauses,
2038
        $expected
2039
    ) {
2040
        $this->assertMultilingualFieldSort(
2041
            $contentDataList,
2042
            $languageSettings,
2043
            $sortClauses,
2044
            $expected,
2045
            false
2046
        );
2047
    }
2048
2049
    /**
2050
     * @param array $contentDataList
2051
     * @param array $languageSettings
2052
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2053
     * @param array $expected
2054
     * @param bool $contentSearch
2055
     */
2056
    protected function assertMultilingualFieldSort(
2057
        array $contentDataList,
2058
        $languageSettings,
2059
        array $sortClauses,
2060
        $expected,
2061
        $contentSearch = true
2062
    ) {
2063
        $this->checkPrioritizedLanguagesSupport();
2064
        $contentType = $this->createTestContentType();
2065
2066
        // Create a draft to account for behaviour with ContentType in different states
2067
        $repository = $this->getRepository();
2068
        $contentTypeService = $repository->getContentTypeService();
2069
        $contentTypeService->createContentTypeDraft($contentType);
2070
2071
        $defaults = [null, null, null, 'eng-GB', false];
2072
        $contentIdList = [];
2073 View Code Duplication
        foreach ($contentDataList as $key => $contentData) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2074
            $contentData = $contentData + $defaults;
2075
            list(
2076
                $fieldValue11,
2077
                $fieldValue12,
2078
                $fieldValue2,
2079
                $mainLanguageCode,
2080
                $alwaysAvailable
2081
            ) = $contentData;
2082
2083
            $contentIdList[$key] = $this->createMultilingualContent(
2084
                $contentType,
2085
                $fieldValue11,
2086
                $fieldValue12,
2087
                $fieldValue2,
2088
                $mainLanguageCode,
2089
                $alwaysAvailable
2090
            )->id;
2091
        }
2092
2093
        // "article" type Content is not matched, this ensures that non-matched
2094
        // field does not affect sort
2095
        $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC);
2096
        array_unshift($sortClauses, $dummySortClause);
2097
        $sortClauses[] = $dummySortClause;
2098
2099
        $searchService = $repository->getSearchService();
2100
        if ($contentSearch) {
2101
            $query = new Query(
2102
                [
2103
                    'query' => new Criterion\ContentTypeId($contentType->id),
2104
                    'sortClauses' => $sortClauses,
2105
                ]
2106
            );
2107
            $result = $searchService->findContent($query, $languageSettings);
2108
        } else {
2109
            $query = new LocationQuery(
2110
                [
2111
                    'query' => new Criterion\ContentTypeId($contentType->id),
2112
                    'sortClauses' => $sortClauses,
2113
                ]
2114
            );
2115
            $result = $searchService->findLocations($query, $languageSettings);
2116
        }
2117
2118
        $this->assertEquals(count($expected), $result->totalCount);
2119
2120
        $expectedIdList = [];
2121
        foreach ($expected as $contentNumber) {
2122
            $expectedIdList[] = $contentIdList[$contentNumber];
2123
        }
2124
2125
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2126
    }
2127
2128
    public function providerForTestMultilingualFieldFilter()
2129
    {
2130
        return [
2131
            0 => [
2132
                $fixture = [
2133
                    1 => [null, 1, null, 'ger-DE', true],
2134
                    2 => [4, null, null, 'eng-GB', true],
2135
                    3 => [3, null, null, 'eng-GB', false],
2136
                    4 => [null, 2, null, 'ger-DE', false],
2137
                    5 => [5, null, null, 'eng-GB', true],
2138
                ],
2139
                $languageSettings = [
2140
                    'languages' => [
2141
                        'ger-DE',
2142
                    ],
2143
                ],
2144
                new Criterion\Field('integer', Criterion\Operator::LT, 5),
2145
                /**
2146
                 * Expected order, Value eng-GB, Value ger-DE.
2147
                 *
2148
                 * Content 2, 4, -
2149
                 * Content 4, -, 2
2150
                 * Content 1, -, 1
2151
                 */
2152
                [2, 4, 1],
2153
            ],
2154
            1 => [
2155
                $fixture,
2156
                [
2157
                    'languages' => [
2158
                        'ger-DE',
2159
                    ],
2160
                    'useAlwaysAvailable' => false,
2161
                ],
2162
                new Criterion\Field('integer', Criterion\Operator::LT, 2),
2163
                /**
2164
                 * Expected order, Value eng-GB, Value ger-DE.
2165
                 *
2166
                 * Content 1, -, 1
2167
                 */
2168
                [1],
2169
            ],
2170
            2 => [
2171
                $fixture,
2172
                [
2173
                    'languages' => [
2174
                        'eng-GB',
2175
                    ],
2176
                ],
2177
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2178
                /**
2179
                 * Expected order, Value eng-GB, Value ger-DE.
2180
                 *
2181
                 * Content 5, 5, -
2182
                 * Content 2, 4, -
2183
                 * Content 3, 3, -
2184
                 * Content 1, -, 1
2185
                 */
2186
                [2, 3, 1],
2187
            ],
2188
            3 => [
2189
                $fixture,
2190
                [
2191
                    'languages' => [
2192
                        'eng-GB',
2193
                    ],
2194
                    'useAlwaysAvailable' => false,
2195
                ],
2196
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2197
                /**
2198
                 * Expected order, Value eng-GB, Value ger-DE.
2199
                 *
2200
                 * Content 2, 4, -
2201
                 * Content 3, 3, -
2202
                 */
2203
                [2, 3],
2204
            ],
2205
            4 => [
2206
                $fixture,
2207
                $languageSettings,
2208
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2209
                /**
2210
                 * Expected order, Value eng-GB, Value ger-DE.
2211
                 *
2212
                 * Content 2, 4, -
2213
                 * Content 4, -, 2
2214
                 * Content 1, -, 1
2215
                 */
2216
                [2, 4, 1],
2217
            ],
2218
            5 => [
2219
                $fixture,
2220
                $languageSettings,
2221
                new Criterion\Field('integer', Criterion\Operator::GT, 1),
2222
                /**
2223
                 * Expected order, Value eng-GB, Value ger-DE.
2224
                 *
2225
                 * Content 5, 5, -
2226
                 * Content 2, 4, -
2227
                 * Content 4, -, 2
2228
                 */
2229
                [5, 2, 4],
2230
            ],
2231
            6 => [
2232
                $fixture,
2233
                $languageSettings,
2234
                new Criterion\Field('integer', Criterion\Operator::GTE, 2),
2235
                /**
2236
                 * Expected order, Value eng-GB, Value ger-DE.
2237
                 *
2238
                 * Content 5, 5, -
2239
                 * Content 2, 4, -
2240
                 * Content 4, -, 2
2241
                 */
2242
                [5, 2, 4],
2243
            ],
2244
            7 => [
2245
                $fixture,
2246
                $languageSettings,
2247
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]),
2248
                /**
2249
                 * Expected order, Value eng-GB, Value ger-DE.
2250
                 *
2251
                 * Content 2, 4, -
2252
                 * Content 4, -, 2
2253
                 */
2254
                [2, 4],
2255
            ],
2256
            8 => [
2257
                $fixture,
2258
                $languageSettings,
2259
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]),
2260
                [],
2261
            ],
2262
            9 => [
2263
                $fixture,
2264
                $languageSettings,
2265
                new Criterion\Field('integer', Criterion\Operator::EQ, 4),
2266
                /**
2267
                 * Expected order, Value eng-GB, Value ger-DE.
2268
                 *
2269
                 * Content 4, -, 2
2270
                 */
2271
                [2],
2272
            ],
2273
            10 => [
2274
                $fixture,
2275
                $languageSettings,
2276
                new Criterion\Field('integer', Criterion\Operator::EQ, 2),
2277
                /**
2278
                 * Expected order, Value eng-GB, Value ger-DE.
2279
                 *
2280
                 * Content 2, 4, -
2281
                 */
2282
                [4],
2283
            ],
2284
        ];
2285
    }
2286
2287
    /**
2288
     * Test for the findContent() method.
2289
     *
2290
     * @group ttt
2291
     * @dataProvider providerForTestMultilingualFieldFilter
2292
     *
2293
     * @param array $contentDataList
2294
     * @param array $languageSettings
2295
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2296
     * @param array $expected
2297
     */
2298
    public function testMultilingualFieldFilterContent(
2299
        array $contentDataList,
2300
        $languageSettings,
2301
        Criterion $criterion,
2302
        $expected
2303
    ) {
2304
        $this->assertMultilingualFieldFilter(
2305
            $contentDataList,
2306
            $languageSettings,
2307
            $criterion,
2308
            $expected
2309
        );
2310
    }
2311
2312
    /**
2313
     * Test for the findLocations() method.
2314
     *
2315
     * @group ttt
2316
     * @dataProvider providerForTestMultilingualFieldFilter
2317
     *
2318
     * @param array $contentDataList
2319
     * @param array $languageSettings
2320
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2321
     * @param array $expected
2322
     */
2323
    public function testMultilingualFieldFilterLocation(
2324
        array $contentDataList,
2325
        $languageSettings,
2326
        Criterion $criterion,
2327
        $expected
2328
    ) {
2329
        $this->assertMultilingualFieldFilter(
2330
            $contentDataList,
2331
            $languageSettings,
2332
            $criterion,
2333
            $expected,
2334
            false
2335
        );
2336
    }
2337
2338
    /**
2339
     * @param array $contentDataList
2340
     * @param array $languageSettings
2341
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2342
     * @param array $expected
2343
     * @param bool $contentSearch
2344
     */
2345
    protected function assertMultilingualFieldFilter(
2346
        array $contentDataList,
2347
        $languageSettings,
2348
        Criterion $criterion,
2349
        $expected,
2350
        $contentSearch = true
2351
    ) {
2352
        $this->checkPrioritizedLanguagesSupport();
2353
        $contentType = $this->createTestContentType();
2354
2355
        // Create a draft to account for behaviour with ContentType in different states
2356
        $repository = $this->getRepository();
2357
        $contentTypeService = $repository->getContentTypeService();
2358
        $contentTypeService->createContentTypeDraft($contentType);
2359
2360
        $defaults = [null, null, null, 'eng-GB', false];
2361
        $contentIdList = [];
2362 View Code Duplication
        foreach ($contentDataList as $key => $contentData) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2363
            $contentData = $contentData + $defaults;
2364
            list(
2365
                $fieldValue11,
2366
                $fieldValue12,
2367
                $fieldValue2,
2368
                $mainLanguageCode,
2369
                $alwaysAvailable
2370
            ) = $contentData;
2371
2372
            $contentIdList[$key] = $this->createMultilingualContent(
2373
                $contentType,
2374
                $fieldValue11,
2375
                $fieldValue12,
2376
                $fieldValue2,
2377
                $mainLanguageCode,
2378
                $alwaysAvailable
2379
            )->id;
2380
        }
2381
2382
        $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC);
2383
        $searchService = $repository->getSearchService();
2384
        if ($contentSearch) {
2385
            $query = new Query(
2386
                [
2387
                    'query' => new Criterion\LogicalAnd(
2388
                        [
2389
                            new Criterion\ContentTypeId($contentType->id),
2390
                            $criterion,
2391
                        ]
2392
                    ),
2393
                    'sortClauses' => [$sortClause],
2394
                ]
2395
            );
2396
            $result = $searchService->findContent($query, $languageSettings);
2397
        } else {
2398
            $query = new LocationQuery(
2399
                [
2400
                    'query' => new Criterion\LogicalAnd(
2401
                        [
2402
                            new Criterion\ContentTypeId($contentType->id),
2403
                            $criterion,
2404
                        ]
2405
                    ),
2406
                    'sortClauses' => [$sortClause],
2407
                ]
2408
            );
2409
            $result = $searchService->findLocations($query, $languageSettings);
2410
        }
2411
2412
        $this->assertEquals(count($expected), $result->totalCount);
2413
2414
        $expectedIdList = [];
2415
        foreach ($expected as $contentNumber) {
2416
            $expectedIdList[] = $contentIdList[$contentNumber];
2417
        }
2418
2419
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2420
    }
2421
2422
    /**
2423
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result
2424
     *
2425
     * @return array
2426
     */
2427
    protected function mapResultContentIds(SearchResult $result)
2428
    {
2429
        return array_map(
2430
            function (SearchHit $searchHit) {
2431
                if ($searchHit->valueObject instanceof Location) {
2432
                    return $searchHit->valueObject->contentInfo->id;
2433
                }
2434
2435
                return $searchHit->valueObject->id;
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2436
            },
2437
            $result->searchHits
2438
        );
2439
    }
2440
2441
    /**
2442
     * Test for the findContent() method.
2443
     *
2444
     * @dataProvider getSortedContentSearches
2445
     *
2446
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2447
     */
2448
    public function testFindAndSortContent($queryData, $fixture, $closure = null)
2449
    {
2450
        $query = new Query($queryData);
2451
        $this->assertQueryFixture($query, $fixture, $closure);
2452
    }
2453
2454
    /**
2455
     * Test for the findContentInfo() method.
2456
     *
2457
     * @dataProvider getSortedContentSearches
2458
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2459
     */
2460
    public function testFindAndSortContentInfo($queryData, $fixture, $closure = null)
2461
    {
2462
        $query = new Query($queryData);
2463
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
2464
    }
2465
2466
    /**
2467
     * Test for the findLocations() method.
2468
     *
2469
     * @dataProvider getSortedContentSearches
2470
     *
2471
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2472
     */
2473
    public function testFindAndSortContentLocations($queryData, $fixture, $closure = null)
2474
    {
2475
        $query = new LocationQuery($queryData);
2476
        $this->assertQueryFixture($query, $fixture, $closure);
2477
    }
2478
2479
    /**
2480
     * Test for the findLocations() method.
2481
     *
2482
     * @dataProvider getSortedLocationSearches
2483
     *
2484
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2485
     */
2486
    public function testFindAndSortLocations($queryData, $fixture, $closure = null)
2487
    {
2488
        $query = new LocationQuery($queryData);
2489
        $this->assertQueryFixture($query, $fixture, $closure);
2490
    }
2491
2492
    /**
2493
     * Test for the findContent() method.
2494
     *
2495
     * @dataProvider getFacetedSearches
2496
     *
2497
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2498
     */
2499
    public function testFindFacetedContent(Query $query, $fixture)
2500
    {
2501
        $this->assertQueryFixture($query, $fixture);
2502
    }
2503
2504
    /**
2505
     * Test for the findContentInfo() method.
2506
     *
2507
     * @dataProvider getFacetedSearches
2508
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2509
     */
2510
    public function testFindFacetedContentInfo(Query $query, $fixture)
2511
    {
2512
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true);
2513
    }
2514
2515
    /**
2516
     * Test for the findContent() method.
2517
     *
2518
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2519
     */
2520 View Code Duplication
    public function testQueryCustomField()
2521
    {
2522
        $query = new Query(
2523
            [
2524
                'query' => new Criterion\CustomField(
2525
                    'custom_field',
2526
                    Criterion\Operator::EQ,
2527
                    'AdMiNiStRaToR'
2528
                ),
2529
                'offset' => 0,
2530
                'limit' => 10,
2531
                'sortClauses' => [new SortClause\ContentId()],
2532
            ]
2533
        );
2534
        $this->assertQueryFixture(
2535
            $query,
2536
            $this->getFixtureDir() . '/QueryCustomField.php'
2537
        );
2538
    }
2539
2540
    /**
2541
     * Test for the findContent() method.
2542
     *
2543
     * This tests explicitely queries the first_name while user is contained in
2544
     * the last_name of admin and anonymous. This is done to show the custom
2545
     * copy field working.
2546
     *
2547
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2548
     */
2549 View Code Duplication
    public function testQueryModifiedField()
2550
    {
2551
        // Check using get_class since the others extend SetupFactory\Legacy
2552
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2553
            $this->markTestIncomplete(
2554
                'Custom fields not supported by LegacySE ' .
2555
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2556
            );
2557
        }
2558
2559
        $query = new Query(
2560
            [
2561
                'query' => new Criterion\Field(
2562
                    'first_name',
2563
                    Criterion\Operator::EQ,
2564
                    'User'
2565
                ),
2566
                'offset' => 0,
2567
                'limit' => 10,
2568
                'sortClauses' => [new SortClause\ContentId()],
2569
            ]
2570
        );
2571
        $query->query->setCustomField('user', 'first_name', 'custom_field');
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class eZ\Publish\API\Repositor...Content\Query\Criterion as the method setCustomField() does only exist in the following sub-classes of eZ\Publish\API\Repositor...Content\Query\Criterion: eZ\Publish\API\Repositor...t\Query\Criterion\Field, eZ\Publish\API\Repositor...uery\Criterion\FullText, eZ\Publish\API\Repositor...ion\MapLocationDistance. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
2572
2573
        $this->assertQueryFixture(
2574
            $query,
2575
            $this->getFixtureDir() . '/QueryModifiedField.php'
2576
        );
2577
    }
2578
2579
    /**
2580
     * Test for the findContent() method.
2581
     *
2582
     * This tests first explicitly creates sort clause on the 'short_name' which is empty
2583
     * for all Content instances of 'folder' ContentType. Custom sort field is then set
2584
     * to the index storage name of folder's 'name' field, in order to show the custom
2585
     * sort field working.
2586
     *
2587
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2588
     */
2589
    public function testSortModifiedField()
2590
    {
2591
        // Check using get_class since the others extend SetupFactory\Legacy
2592
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2593
            $this->markTestIncomplete(
2594
                'Custom field sort not supported by LegacySE ' .
2595
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2596
            );
2597
        }
2598
2599
        $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC);
2600
        $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s');
2601
2602
        $query = new Query(
2603
            [
2604
                'filter' => new Criterion\ContentTypeId(1),
2605
                'offset' => 0,
2606
                'limit' => 10,
2607
                'sortClauses' => [
2608
                    $sortClause,
2609
                    new SortClause\ContentId(),
2610
                ],
2611
            ]
2612
        );
2613
2614
        $this->assertQueryFixture(
2615
            $query,
2616
            $this->getFixtureDir() . '/SortFolderName.php'
2617
        );
2618
    }
2619
2620
    /**
2621
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
2622
     */
2623 View Code Duplication
    protected function createTestPlaceContentType()
2624
    {
2625
        $repository = $this->getRepository();
2626
        $contentTypeService = $repository->getContentTypeService();
2627
2628
        $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype');
2629
        $createStruct->mainLanguageCode = 'eng-GB';
2630
        $createStruct->names = ['eng-GB' => 'Test type'];
2631
        $createStruct->creatorId = 14;
2632
        $createStruct->creationDate = new \DateTime();
2633
2634
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation');
2635
        $translatableFieldCreate->names = ['eng-GB' => 'Map location field'];
2636
        $translatableFieldCreate->fieldGroup = 'main';
2637
        $translatableFieldCreate->position = 1;
2638
        $translatableFieldCreate->isTranslatable = false;
2639
        $translatableFieldCreate->isSearchable = true;
2640
2641
        $createStruct->addFieldDefinition($translatableFieldCreate);
2642
2643
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
2644
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
2645
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
2646
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
2647
2648
        return $contentType;
2649
    }
2650
2651
    /**
2652
     * Test for the findContent() method.
2653
     *
2654
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2655
     * @group maplocation
2656
     */
2657 View Code Duplication
    public function testMapLocationDistanceLessThanOrEqual()
2658
    {
2659
        $contentType = $this->createTestPlaceContentType();
2660
2661
        // Create a draft to account for behaviour with ContentType in different states
2662
        $repository = $this->getRepository();
2663
        $contentTypeService = $repository->getContentTypeService();
2664
        $contentService = $repository->getContentService();
2665
        $contentTypeService->createContentTypeDraft($contentType);
2666
2667
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2668
        $createStruct->alwaysAvailable = false;
2669
        $createStruct->mainLanguageCode = 'eng-GB';
2670
        $createStruct->setField(
2671
            'maplocation',
2672
            [
2673
                'latitude' => 45.894877,
2674
                'longitude' => 15.972699,
2675
                'address' => 'Here be wild boars',
2676
            ],
2677
            'eng-GB'
2678
        );
2679
2680
        $draft = $contentService->createContent($createStruct);
2681
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2682
2683
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2684
        $createStruct->alwaysAvailable = false;
2685
        $createStruct->mainLanguageCode = 'eng-GB';
2686
        $createStruct->setField(
2687
            'maplocation',
2688
            [
2689
                'latitude' => 45.927334,
2690
                'longitude' => 15.934847,
2691
                'address' => 'A lone tree',
2692
            ],
2693
            'eng-GB'
2694
        );
2695
2696
        $draft = $contentService->createContent($createStruct);
2697
        $tree = $contentService->publishVersion($draft->getVersionInfo());
0 ignored issues
show
Unused Code introduced by
$tree is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2698
2699
        $this->refreshSearch($repository);
2700
2701
        $query = new Query(
2702
            [
2703
                'filter' => new Criterion\LogicalAnd(
2704
                    [
2705
                        new Criterion\ContentTypeId($contentType->id),
2706
                        new Criterion\MapLocationDistance(
2707
                            'maplocation',
2708
                            Criterion\Operator::LTE,
2709
                            240,
2710
                            43.756825,
2711
                            15.775074
2712
                        ),
2713
                    ]
2714
                ),
2715
                'offset' => 0,
2716
                'limit' => 10,
2717
                'sortClauses' => [],
2718
            ]
2719
        );
2720
2721
        $searchService = $repository->getSearchService();
2722
        $result = $searchService->findContent($query);
2723
2724
        $this->assertEquals(1, $result->totalCount);
2725
        $this->assertEquals(
2726
            $wildBoars->id,
2727
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2728
        );
2729
    }
2730
2731
    /**
2732
     * Test for the findContent() method.
2733
     *
2734
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2735
     * @group maplocation
2736
     */
2737 View Code Duplication
    public function testMapLocationDistanceGreaterThanOrEqual()
2738
    {
2739
        $contentType = $this->createTestPlaceContentType();
2740
2741
        // Create a draft to account for behaviour with ContentType in different states
2742
        $repository = $this->getRepository();
2743
        $contentTypeService = $repository->getContentTypeService();
2744
        $contentService = $repository->getContentService();
2745
        $contentTypeService->createContentTypeDraft($contentType);
2746
2747
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2748
        $createStruct->alwaysAvailable = false;
2749
        $createStruct->mainLanguageCode = 'eng-GB';
2750
        $createStruct->setField(
2751
            'maplocation',
2752
            [
2753
                'latitude' => 45.894877,
2754
                'longitude' => 15.972699,
2755
                'address' => 'Here be wild boars',
2756
            ],
2757
            'eng-GB'
2758
        );
2759
2760
        $draft = $contentService->createContent($createStruct);
2761
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
0 ignored issues
show
Unused Code introduced by
$wildBoars is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2762
2763
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2764
        $createStruct->alwaysAvailable = false;
2765
        $createStruct->mainLanguageCode = 'eng-GB';
2766
        $createStruct->setField(
2767
            'maplocation',
2768
            [
2769
                'latitude' => 45.927334,
2770
                'longitude' => 15.934847,
2771
                'address' => 'A lone tree',
2772
            ],
2773
            'eng-GB'
2774
        );
2775
2776
        $draft = $contentService->createContent($createStruct);
2777
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2778
2779
        $this->refreshSearch($repository);
2780
2781
        $query = new Query(
2782
            [
2783
                'filter' => new Criterion\LogicalAnd(
2784
                    [
2785
                        new Criterion\ContentTypeId($contentType->id),
2786
                        new Criterion\MapLocationDistance(
2787
                            'maplocation',
2788
                            Criterion\Operator::GTE,
2789
                            240,
2790
                            43.756825,
2791
                            15.775074
2792
                        ),
2793
                    ]
2794
                ),
2795
                'offset' => 0,
2796
                'limit' => 10,
2797
                'sortClauses' => [],
2798
            ]
2799
        );
2800
2801
        $searchService = $repository->getSearchService();
2802
        $result = $searchService->findContent($query);
2803
2804
        $this->assertEquals(1, $result->totalCount);
2805
        $this->assertEquals(
2806
            $tree->id,
2807
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2808
        );
2809
    }
2810
2811
    /**
2812
     * Test for the findContent() method.
2813
     *
2814
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2815
     * @group maplocation
2816
     */
2817
    public function testMapLocationDistanceBetween()
2818
    {
2819
        $contentType = $this->createTestPlaceContentType();
2820
2821
        // Create a draft to account for behaviour with ContentType in different states
2822
        $repository = $this->getRepository();
2823
        $contentTypeService = $repository->getContentTypeService();
2824
        $contentService = $repository->getContentService();
2825
        $contentTypeService->createContentTypeDraft($contentType);
2826
2827
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2828
        $createStruct->alwaysAvailable = false;
2829
        $createStruct->mainLanguageCode = 'eng-GB';
2830
        $createStruct->setField(
2831
            'maplocation',
2832
            [
2833
                'latitude' => 45.894877,
2834
                'longitude' => 15.972699,
2835
                'address' => 'Here be wild boars',
2836
            ],
2837
            'eng-GB'
2838
        );
2839
2840
        $draft = $contentService->createContent($createStruct);
2841
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
0 ignored issues
show
Unused Code introduced by
$wildBoars is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2842
2843
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2844
        $createStruct->alwaysAvailable = false;
2845
        $createStruct->mainLanguageCode = 'eng-GB';
2846
        $createStruct->setField(
2847
            'maplocation',
2848
            [
2849
                'latitude' => 45.927334,
2850
                'longitude' => 15.934847,
2851
                'address' => 'A lone tree',
2852
            ],
2853
            'eng-GB'
2854
        );
2855
2856
        $draft = $contentService->createContent($createStruct);
2857
        $tree = $contentService->publishVersion($draft->getVersionInfo());
0 ignored issues
show
Unused Code introduced by
$tree is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2858
2859
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2860
        $createStruct->alwaysAvailable = false;
2861
        $createStruct->mainLanguageCode = 'eng-GB';
2862
        $createStruct->setField(
2863
            'maplocation',
2864
            [
2865
                'latitude' => 45.903777,
2866
                'longitude' => 15.958788,
2867
                'address' => 'Meadow with mushrooms',
2868
            ],
2869
            'eng-GB'
2870
        );
2871
2872
        $draft = $contentService->createContent($createStruct);
2873
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
2874
2875
        $this->refreshSearch($repository);
2876
2877
        $query = new Query(
2878
            [
2879
                'filter' => new Criterion\LogicalAnd(
2880
                    [
2881
                        new Criterion\ContentTypeId($contentType->id),
2882
                        new Criterion\MapLocationDistance(
2883
                            'maplocation',
2884
                            Criterion\Operator::BETWEEN,
2885
                            [239, 241],
2886
                            43.756825,
2887
                            15.775074
2888
                        ),
2889
                    ]
2890
                ),
2891
                'offset' => 0,
2892
                'limit' => 10,
2893
                'sortClauses' => [],
2894
            ]
2895
        );
2896
2897
        $searchService = $repository->getSearchService();
2898
        $result = $searchService->findContent($query);
2899
2900
        $this->assertEquals(1, $result->totalCount);
2901
        $this->assertEquals(
2902
            $mushrooms->id,
2903
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2904
        );
2905
    }
2906
2907
    /**
2908
     * Test for the findContent() method.
2909
     *
2910
     * This tests the distance over the pole. The tests intentionally uses large range,
2911
     * as the flat Earth model used in Legacy Storage Search is not precise for the use case.
2912
     * What is tested here is that outer bounding box is correctly calculated, so that
2913
     * location is not excluded.
2914
     *
2915
     * Range between 222km and 350km shows the magnitude of error between great-circle
2916
     * (always very precise) and flat Earth (very imprecise for this use case) models.
2917
     *
2918
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2919
     * @group maplocation
2920
     */
2921
    public function testMapLocationDistanceBetweenPolar()
2922
    {
2923
        $contentType = $this->createTestPlaceContentType();
2924
2925
        // Create a draft to account for behaviour with ContentType in different states
2926
        $repository = $this->getRepository();
2927
        $contentTypeService = $repository->getContentTypeService();
2928
        $contentService = $repository->getContentService();
2929
        $contentTypeService->createContentTypeDraft($contentType);
2930
2931
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2932
        $createStruct->alwaysAvailable = false;
2933
        $createStruct->mainLanguageCode = 'eng-GB';
2934
        $createStruct->setField(
2935
            'maplocation',
2936
            [
2937
                'latitude' => 89,
2938
                'longitude' => -164,
2939
                'address' => 'Polar bear media tower',
2940
            ],
2941
            'eng-GB'
2942
        );
2943
2944
        $draft = $contentService->createContent($createStruct);
2945
        $polarBear = $contentService->publishVersion($draft->getVersionInfo());
2946
2947
        $this->refreshSearch($repository);
2948
2949
        $query = new Query(
2950
            [
2951
                'filter' => new Criterion\LogicalAnd(
2952
                    [
2953
                        new Criterion\ContentTypeId($contentType->id),
2954
                        new Criterion\MapLocationDistance(
2955
                            'maplocation',
2956
                            Criterion\Operator::BETWEEN,
2957
                            [221, 350],
2958
                            89,
2959
                            16
2960
                        ),
2961
                    ]
2962
                ),
2963
                'offset' => 0,
2964
                'limit' => 10,
2965
                'sortClauses' => [],
2966
            ]
2967
        );
2968
2969
        $searchService = $repository->getSearchService();
2970
        $result = $searchService->findContent($query);
2971
2972
        $this->assertEquals(1, $result->totalCount);
2973
        $this->assertEquals(
2974
            $polarBear->id,
2975
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2976
        );
2977
    }
2978
2979
    /**
2980
     * Test for the findContent() method.
2981
     *
2982
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2983
     * @group maplocation
2984
     */
2985 View Code Duplication
    public function testMapLocationDistanceSortAscending()
2986
    {
2987
        $contentType = $this->createTestPlaceContentType();
2988
2989
        // Create a draft to account for behaviour with ContentType in different states
2990
        $repository = $this->getRepository();
2991
        $contentTypeService = $repository->getContentTypeService();
2992
        $contentService = $repository->getContentService();
2993
        $contentTypeService->createContentTypeDraft($contentType);
2994
2995
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2996
        $createStruct->alwaysAvailable = false;
2997
        $createStruct->mainLanguageCode = 'eng-GB';
2998
        $createStruct->setField(
2999
            'maplocation',
3000
            [
3001
                'latitude' => 45.894877,
3002
                'longitude' => 15.972699,
3003
                'address' => 'Here be wild boars',
3004
            ],
3005
            'eng-GB'
3006
        );
3007
3008
        $draft = $contentService->createContent($createStruct);
3009
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3010
3011
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3012
        $createStruct->alwaysAvailable = false;
3013
        $createStruct->mainLanguageCode = 'eng-GB';
3014
        $createStruct->setField(
3015
            'maplocation',
3016
            [
3017
                'latitude' => 45.927334,
3018
                'longitude' => 15.934847,
3019
                'address' => 'A lone tree',
3020
            ],
3021
            'eng-GB'
3022
        );
3023
3024
        $draft = $contentService->createContent($createStruct);
3025
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3026
3027
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3028
        $createStruct->alwaysAvailable = false;
3029
        $createStruct->mainLanguageCode = 'eng-GB';
3030
        $createStruct->setField(
3031
            'maplocation',
3032
            [
3033
                'latitude' => 45.903777,
3034
                'longitude' => 15.958788,
3035
                'address' => 'Meadow with mushrooms',
3036
            ],
3037
            'eng-GB'
3038
        );
3039
3040
        $draft = $contentService->createContent($createStruct);
3041
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3042
3043
        $this->refreshSearch($repository);
3044
3045
        $wellInVodice = [
3046
            'latitude' => 43.756825,
3047
            'longitude' => 15.775074,
3048
        ];
3049
3050
        $query = new Query(
3051
            [
3052
                'filter' => new Criterion\LogicalAnd(
3053
                    [
3054
                        new Criterion\ContentTypeId($contentType->id),
3055
                        new Criterion\MapLocationDistance(
3056
                            'maplocation',
3057
                            Criterion\Operator::GTE,
3058
                            235,
3059
                            $wellInVodice['latitude'],
3060
                            $wellInVodice['longitude']
3061
                        ),
3062
                    ]
3063
                ),
3064
                'offset' => 0,
3065
                'limit' => 10,
3066
                'sortClauses' => [
3067
                    new SortClause\MapLocationDistance(
3068
                        'testtype',
3069
                        'maplocation',
3070
                        $wellInVodice['latitude'],
3071
                        $wellInVodice['longitude'],
3072
                        Query::SORT_ASC
3073
                    ),
3074
                ],
3075
            ]
3076
        );
3077
3078
        $searchService = $repository->getSearchService();
3079
        $result = $searchService->findContent($query);
3080
3081
        $this->assertEquals(3, $result->totalCount);
3082
        $this->assertEquals(
3083
            $wildBoars->id,
3084
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3085
        );
3086
        $this->assertEquals(
3087
            $mushrooms->id,
3088
            $result->searchHits[1]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3089
        );
3090
        $this->assertEquals(
3091
            $tree->id,
3092
            $result->searchHits[2]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3093
        );
3094
    }
3095
3096
    /**
3097
     * Test for the findContent() method.
3098
     *
3099
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3100
     * @group maplocation
3101
     */
3102 View Code Duplication
    public function testMapLocationDistanceSortDescending()
3103
    {
3104
        $contentType = $this->createTestPlaceContentType();
3105
3106
        // Create a draft to account for behaviour with ContentType in different states
3107
        $repository = $this->getRepository();
3108
        $contentTypeService = $repository->getContentTypeService();
3109
        $contentService = $repository->getContentService();
3110
        $contentTypeService->createContentTypeDraft($contentType);
3111
3112
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3113
        $createStruct->alwaysAvailable = false;
3114
        $createStruct->mainLanguageCode = 'eng-GB';
3115
        $createStruct->setField(
3116
            'maplocation',
3117
            [
3118
                'latitude' => 45.894877,
3119
                'longitude' => 15.972699,
3120
                'address' => 'Here be wild boars',
3121
            ],
3122
            'eng-GB'
3123
        );
3124
3125
        $draft = $contentService->createContent($createStruct);
3126
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3127
3128
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3129
        $createStruct->alwaysAvailable = false;
3130
        $createStruct->mainLanguageCode = 'eng-GB';
3131
        $createStruct->setField(
3132
            'maplocation',
3133
            [
3134
                'latitude' => 45.927334,
3135
                'longitude' => 15.934847,
3136
                'address' => 'A lone tree',
3137
            ],
3138
            'eng-GB'
3139
        );
3140
3141
        $draft = $contentService->createContent($createStruct);
3142
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3143
3144
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3145
        $createStruct->alwaysAvailable = false;
3146
        $createStruct->mainLanguageCode = 'eng-GB';
3147
        $createStruct->setField(
3148
            'maplocation',
3149
            [
3150
                'latitude' => 45.903777,
3151
                'longitude' => 15.958788,
3152
                'address' => 'Meadow with mushrooms',
3153
            ],
3154
            'eng-GB'
3155
        );
3156
3157
        $draft = $contentService->createContent($createStruct);
3158
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3159
3160
        $this->refreshSearch($repository);
3161
3162
        $well = [
3163
            'latitude' => 43.756825,
3164
            'longitude' => 15.775074,
3165
        ];
3166
3167
        $query = new Query(
3168
            [
3169
                'filter' => new Criterion\LogicalAnd(
3170
                    [
3171
                        new Criterion\ContentTypeId($contentType->id),
3172
                        new Criterion\MapLocationDistance(
3173
                            'maplocation',
3174
                            Criterion\Operator::GTE,
3175
                            235,
3176
                            $well['latitude'],
3177
                            $well['longitude']
3178
                        ),
3179
                    ]
3180
                ),
3181
                'offset' => 0,
3182
                'limit' => 10,
3183
                'sortClauses' => [
3184
                    new SortClause\MapLocationDistance(
3185
                        'testtype',
3186
                        'maplocation',
3187
                        $well['latitude'],
3188
                        $well['longitude'],
3189
                        Query::SORT_DESC
3190
                    ),
3191
                ],
3192
            ]
3193
        );
3194
3195
        $searchService = $repository->getSearchService();
3196
        $result = $searchService->findContent($query);
3197
3198
        $this->assertEquals(3, $result->totalCount);
3199
        $this->assertEquals(
3200
            $wildBoars->id,
3201
            $result->searchHits[2]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3202
        );
3203
        $this->assertEquals(
3204
            $mushrooms->id,
3205
            $result->searchHits[1]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3206
        );
3207
        $this->assertEquals(
3208
            $tree->id,
3209
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3210
        );
3211
    }
3212
3213
    /**
3214
     * Test for the findContent() method.
3215
     *
3216
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3217
     * @group maplocation
3218
     */
3219
    public function testMapLocationDistanceWithCustomField()
3220
    {
3221
        $setupFactory = $this->getSetupFactory();
3222
        if ($setupFactory instanceof LegacyElasticsearch) {
3223
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3224
        }
3225
3226
        $contentType = $this->createTestPlaceContentType();
3227
3228
        // Create a draft to account for behaviour with ContentType in different states
3229
        $repository = $this->getRepository();
3230
        $contentTypeService = $repository->getContentTypeService();
3231
        $contentService = $repository->getContentService();
3232
        $contentTypeService->createContentTypeDraft($contentType);
3233
3234
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3235
        $createStruct->alwaysAvailable = false;
3236
        $createStruct->mainLanguageCode = 'eng-GB';
3237
        $createStruct->setField(
3238
            'maplocation',
3239
            [
3240
                'latitude' => 45.894877,
3241
                'longitude' => 15.972699,
3242
                'address' => 'Here be wild boars',
3243
            ],
3244
            'eng-GB'
3245
        );
3246
3247
        $draft = $contentService->createContent($createStruct);
3248
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3249
3250
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3251
        $createStruct->alwaysAvailable = false;
3252
        $createStruct->mainLanguageCode = 'eng-GB';
3253
        $createStruct->setField(
3254
            'maplocation',
3255
            [
3256
                'latitude' => 45.927334,
3257
                'longitude' => 15.934847,
3258
                'address' => 'A lone tree',
3259
            ],
3260
            'eng-GB'
3261
        );
3262
3263
        $draft = $contentService->createContent($createStruct);
3264
        $tree = $contentService->publishVersion($draft->getVersionInfo());
0 ignored issues
show
Unused Code introduced by
$tree is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3265
3266
        $this->refreshSearch($repository);
3267
3268
        $distanceCriterion = new Criterion\MapLocationDistance(
3269
            'maplocation',
3270
            Criterion\Operator::LTE,
3271
            240,
3272
            43.756825,
3273
            15.775074
3274
        );
3275
        $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3276
3277
        $query = new Query(
3278
            [
3279
                'filter' => new Criterion\LogicalAnd(
3280
                    [
3281
                        new Criterion\ContentTypeId($contentType->id),
3282
                        $distanceCriterion,
3283
                    ]
3284
                ),
3285
                'offset' => 0,
3286
                'limit' => 10,
3287
                'sortClauses' => [],
3288
            ]
3289
        );
3290
3291
        $searchService = $repository->getSearchService();
3292
        $result = $searchService->findContent($query);
3293
3294
        $this->assertEquals(1, $result->totalCount);
3295
        $this->assertEquals(
3296
            $wildBoars->id,
3297
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3298
        );
3299
    }
3300
3301
    /**
3302
     * Test for the findContent() method.
3303
     *
3304
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3305
     * @group maplocation
3306
     */
3307
    public function testMapLocationDistanceWithCustomFieldSort()
3308
    {
3309
        $setupFactory = $this->getSetupFactory();
3310
        if ($setupFactory instanceof LegacyElasticsearch) {
3311
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3312
        }
3313
3314
        $contentType = $this->createTestPlaceContentType();
3315
3316
        // Create a draft to account for behaviour with ContentType in different states
3317
        $repository = $this->getRepository();
3318
        $contentTypeService = $repository->getContentTypeService();
3319
        $contentService = $repository->getContentService();
3320
        $contentTypeService->createContentTypeDraft($contentType);
3321
3322
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3323
        $createStruct->alwaysAvailable = false;
3324
        $createStruct->mainLanguageCode = 'eng-GB';
3325
        $createStruct->setField(
3326
            'maplocation',
3327
            [
3328
                'latitude' => 45.894877,
3329
                'longitude' => 15.972699,
3330
                'address' => 'Here be wild boars',
3331
            ],
3332
            'eng-GB'
3333
        );
3334
3335
        $draft = $contentService->createContent($createStruct);
3336
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3337
3338
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3339
        $createStruct->alwaysAvailable = false;
3340
        $createStruct->mainLanguageCode = 'eng-GB';
3341
        $createStruct->setField(
3342
            'maplocation',
3343
            [
3344
                'latitude' => 45.927334,
3345
                'longitude' => 15.934847,
3346
                'address' => 'A lone tree',
3347
            ],
3348
            'eng-GB'
3349
        );
3350
3351
        $draft = $contentService->createContent($createStruct);
3352
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3353
3354
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3355
        $createStruct->alwaysAvailable = false;
3356
        $createStruct->mainLanguageCode = 'eng-GB';
3357
        $createStruct->setField(
3358
            'maplocation',
3359
            [
3360
                'latitude' => 45.903777,
3361
                'longitude' => 15.958788,
3362
                'address' => 'Meadow with mushrooms',
3363
            ],
3364
            'eng-GB'
3365
        );
3366
3367
        $draft = $contentService->createContent($createStruct);
3368
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3369
3370
        $this->refreshSearch($repository);
3371
3372
        $well = [
3373
            'latitude' => 43.756825,
3374
            'longitude' => 15.775074,
3375
        ];
3376
3377
        $sortClause = new SortClause\MapLocationDistance(
3378
            'testtype',
3379
            'maplocation',
3380
            $well['latitude'],
3381
            $well['longitude'],
3382
            Query::SORT_DESC
3383
        );
3384
        $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3385
3386
        $query = new Query(
3387
            [
3388
                'filter' => new Criterion\LogicalAnd(
3389
                    [
3390
                        new Criterion\ContentTypeId($contentType->id),
3391
                        new Criterion\MapLocationDistance(
3392
                            'maplocation',
3393
                            Criterion\Operator::GTE,
3394
                            235,
3395
                            $well['latitude'],
3396
                            $well['longitude']
3397
                        ),
3398
                    ]
3399
                ),
3400
                'offset' => 0,
3401
                'limit' => 10,
3402
                'sortClauses' => [
3403
                    $sortClause,
3404
                ],
3405
            ]
3406
        );
3407
3408
        $searchService = $repository->getSearchService();
3409
        $result = $searchService->findContent($query);
3410
3411
        $this->assertEquals(3, $result->totalCount);
3412
        $this->assertEquals(
3413
            $wildBoars->id,
3414
            $result->searchHits[2]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3415
        );
3416
        $this->assertEquals(
3417
            $mushrooms->id,
3418
            $result->searchHits[1]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3419
        );
3420
        $this->assertEquals(
3421
            $tree->id,
3422
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3423
        );
3424
    }
3425
3426
    /**
3427
     * Test for the findLocations() method.
3428
     *
3429
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3430
     */
3431 View Code Duplication
    public function testFindMainLocation()
3432
    {
3433
        $plainSiteLocationId = 56;
3434
        $designLocationId = 58;
3435
        $partnersContentId = 59;
3436
        $repository = $this->getRepository();
3437
        $locationService = $repository->getLocationService();
3438
        $contentService = $repository->getContentService();
3439
3440
        // Add secondary Location for "Partners" user group, under "Design" page
3441
        $locationService->createLocation(
3442
            $contentService->loadContentInfo($partnersContentId),
3443
            $locationService->newLocationCreateStruct($designLocationId)
3444
        );
3445
3446
        $this->refreshSearch($repository);
3447
3448
        $query = new LocationQuery(
3449
            [
3450
                'filter' => new Criterion\LogicalAnd(
3451
                    [
3452
                        new Criterion\ParentLocationId($designLocationId),
3453
                        new Criterion\Location\IsMainLocation(
3454
                            Criterion\Location\IsMainLocation::MAIN
3455
                        ),
3456
                    ]
3457
                ),
3458
                'offset' => 0,
3459
                'limit' => 10,
3460
                'sortClauses' => [],
3461
            ]
3462
        );
3463
3464
        $searchService = $repository->getSearchService();
3465
        $result = $searchService->findLocations($query);
3466
3467
        $this->assertEquals(1, $result->totalCount);
3468
        $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3469
    }
3470
3471
    /**
3472
     * Test for the findLocations() method.
3473
     *
3474
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3475
     */
3476 View Code Duplication
    public function testFindNonMainLocation()
3477
    {
3478
        $designLocationId = 58;
3479
        $partnersContentId = 59;
3480
        $repository = $this->getRepository();
3481
        $locationService = $repository->getLocationService();
3482
        $contentService = $repository->getContentService();
3483
3484
        // Add secondary Location for "Partners" user group, under "Design" page
3485
        $newLocation = $locationService->createLocation(
3486
            $contentService->loadContentInfo($partnersContentId),
3487
            $locationService->newLocationCreateStruct($designLocationId)
3488
        );
3489
3490
        $this->refreshSearch($repository);
3491
3492
        $query = new LocationQuery(
3493
            [
3494
                'filter' => new Criterion\LogicalAnd(
3495
                    [
3496
                        new Criterion\ParentLocationId($designLocationId),
3497
                        new Criterion\Location\IsMainLocation(
3498
                            Criterion\Location\IsMainLocation::NOT_MAIN
3499
                        ),
3500
                    ]
3501
                ),
3502
                'offset' => 0,
3503
                'limit' => 10,
3504
                'sortClauses' => [],
3505
            ]
3506
        );
3507
3508
        $searchService = $repository->getSearchService();
3509
        $result = $searchService->findLocations($query);
3510
3511
        $this->assertEquals(1, $result->totalCount);
3512
        $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3513
    }
3514
3515
    /**
3516
     * Test for the findLocations() method.
3517
     *
3518
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3519
     */
3520 View Code Duplication
    public function testSortMainLocationAscending()
3521
    {
3522
        $plainSiteLocationId = 56;
3523
        $designLocationId = 58;
3524
        $partnersContentId = 59;
3525
        $repository = $this->getRepository();
3526
        $locationService = $repository->getLocationService();
3527
        $contentService = $repository->getContentService();
3528
3529
        // Add secondary Location for "Partners" user group, under "Design" page
3530
        $newLocation = $locationService->createLocation(
3531
            $contentService->loadContentInfo($partnersContentId),
3532
            $locationService->newLocationCreateStruct($designLocationId)
3533
        );
3534
3535
        $this->refreshSearch($repository);
3536
3537
        $query = new LocationQuery(
3538
            [
3539
                'filter' => new Criterion\ParentLocationId($designLocationId),
3540
                'offset' => 0,
3541
                'limit' => 10,
3542
                'sortClauses' => [
3543
                    new SortClause\Location\IsMainLocation(
3544
                        LocationQuery::SORT_ASC
3545
                    ),
3546
                ],
3547
            ]
3548
        );
3549
3550
        $searchService = $repository->getSearchService();
3551
        $result = $searchService->findLocations($query);
3552
3553
        $this->assertEquals(2, $result->totalCount);
3554
        $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3555
        $this->assertEquals($plainSiteLocationId, $result->searchHits[1]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3556
    }
3557
3558
    /**
3559
     * Test for the findLocations() method.
3560
     *
3561
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3562
     */
3563 View Code Duplication
    public function testSortMainLocationDescending()
3564
    {
3565
        $plainSiteLocationId = 56;
3566
        $designLocationId = 58;
3567
        $partnersContentId = 59;
3568
        $repository = $this->getRepository();
3569
        $locationService = $repository->getLocationService();
3570
        $contentService = $repository->getContentService();
3571
3572
        // Add secondary Location for "Partners" user group, under "Design" page
3573
        $newLocation = $locationService->createLocation(
3574
            $contentService->loadContentInfo($partnersContentId),
3575
            $locationService->newLocationCreateStruct($designLocationId)
3576
        );
3577
3578
        $this->refreshSearch($repository);
3579
3580
        $query = new LocationQuery(
3581
            [
3582
                'filter' => new Criterion\ParentLocationId($designLocationId),
3583
                'offset' => 0,
3584
                'limit' => 10,
3585
                'sortClauses' => [
3586
                    new SortClause\Location\IsMainLocation(
3587
                        LocationQuery::SORT_DESC
3588
                    ),
3589
                ],
3590
            ]
3591
        );
3592
3593
        $searchService = $repository->getSearchService();
3594
        $result = $searchService->findLocations($query);
3595
3596
        $this->assertEquals(2, $result->totalCount);
3597
        $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3598
        $this->assertEquals($newLocation->id, $result->searchHits[1]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3599
    }
3600
3601
    /**
3602
     * Test for the findLocations() method.
3603
     *
3604
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3605
     */
3606
    public function testContentWithMultipleLocations()
3607
    {
3608
        $repository = $this->getRepository();
3609
        $contentService = $repository->getContentService();
3610
        $contentTypeService = $repository->getContentTypeService();
3611
        $locationService = $repository->getLocationService();
3612
3613
        $forumType = $contentTypeService->loadContentTypeByIdentifier('forum');
3614
3615
        $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB');
3616
        $createStruct->alwaysAvailable = false;
3617
        $createStruct->setField('name', 'An awesome duplicate forum');
3618
3619
        $draft = $contentService->createContent($createStruct);
3620
        $content = $contentService->publishVersion($draft->getVersionInfo());
3621
3622
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
3623
        $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3624
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5);
3625
        $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3626
3627
        $this->refreshSearch($repository);
3628
3629
        $query = new LocationQuery(
3630
            [
3631
                'filter' => new Criterion\ContentId($content->id),
3632
                'sortClauses' => [
3633
                    new SortClause\Location\Id(LocationQuery::SORT_ASC),
3634
                ],
3635
            ]
3636
        );
3637
3638
        $searchService = $repository->getSearchService();
3639
        $result = $searchService->findLocations($query);
3640
3641
        $this->assertEquals(2, $result->totalCount);
3642
        $this->assertEquals(
3643
            $location1->id,
3644
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3645
        );
3646
        $this->assertEquals(
3647
            $location2->id,
3648
            $result->searchHits[1]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3649
        );
3650
    }
3651
3652
    protected function createContentForTestUserMetadataGroupHorizontal()
3653
    {
3654
        $repository = $this->getRepository();
3655
        $contentService = $repository->getContentService();
3656
        $contentTypeService = $repository->getContentTypeService();
3657
        $locationService = $repository->getLocationService();
3658
        $userService = $repository->getUserService();
3659
        $administratorUser = $repository->getCurrentUser();
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3660
        // ID of the "Administrators" user group in an eZ Publish demo installation
3661
        $administratorsUserGroupId = 12;
3662
        // ID of the "Editors" user group in an eZ Publish demo installation
3663
        $editorsUserGroupId = 13;
3664
3665
        $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId);
3666
        $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId);
3667
3668
        // Add additional Location for Administrators UserGroup under Editors UserGroup Location
3669
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3670
            $editorsUserGroup->contentInfo->mainLocationId
3671
        );
3672
        $newAdministratorsUserGroupLocation = $locationService->createLocation(
3673
            $administratorsUserGroup->contentInfo,
3674
            $locationCreateStruct
3675
        );
3676
3677
        // Add additional Location for administrator user under newly created UserGroup Location
3678
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3679
            $newAdministratorsUserGroupLocation->id
3680
        );
3681
        $locationService->createLocation(
3682
            $administratorUser->contentInfo,
3683
            $locationCreateStruct
3684
        );
3685
3686
        // Create a Content to be found through Editors UserGroup id.
3687
        // This ensures data is indexed, it could also be done by updating metadata of
3688
        // an existing Content, but slot would need to reindex Content and that should
3689
        // be tested elsewhere (dedicated indexing integration tests, missing ATM).
3690
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
3691
3692
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3693
        $createStruct->setField('name', 'test');
3694
3695
        $locationCreateStruct = $locationService->newLocationCreateStruct(2);
3696
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
3697
        $content = $contentService->publishVersion($draft->getVersionInfo());
3698
        $contentTypeService->createContentTypeDraft($contentType);
3699
3700
        $this->refreshSearch($repository);
3701
3702
        return $content;
3703
    }
3704
3705
    /**
3706
     * Test for the findContent() method.
3707
     *
3708
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3709
     */
3710
    public function testUserMetadataGroupHorizontalFilterContent($queryType = null)
3711
    {
3712
        if ($queryType === null) {
3713
            $queryType = 'filter';
3714
        }
3715
3716
        $repository = $this->getRepository();
3717
        $searchService = $repository->getSearchService();
3718
        $editorsUserGroupId = 13;
3719
3720
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3721
3722
        $criteria = [];
3723
        $setupFactory = $this->getSetupFactory();
3724
3725
        // Do not limit for LSE, as it does not not require reindexing.
3726
        // See explanation below.
3727
        if ($setupFactory instanceof LegacySolrSetupFactory || $setupFactory instanceof LegacyElasticsearch) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3728
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3729
        }
3730
3731
        $criteria[] = new Criterion\UserMetadata(
3732
            Criterion\UserMetadata::GROUP,
3733
            Criterion\Operator::EQ,
3734
            $editorsUserGroupId
3735
        );
3736
3737
        $query = new Query(
3738
            [
3739
                $queryType => new Criterion\LogicalAnd($criteria),
3740
                'sortClauses' => [
3741
                    new SortClause\ContentId(),
3742
                ],
3743
                'limit' => 50,
3744
            ]
3745
        );
3746
3747
        if ($setupFactory instanceof LegacySolrSetupFactory || $setupFactory instanceof LegacyElasticsearch) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3748
            $result = $searchService->findContent($query);
3749
3750
            // Administrator User is owned by itself, when additional Locations are added
3751
            // it should be reindexed and its UserGroups will updated, which means it should
3752
            // also be found as a Content of Editors UserGroup. However we do not handle this
3753
            // in slots yet, and also miss SPI methods to do it without using Search (also
3754
            // needed to decouple services), because as indexing is asynchronous Search
3755
            // should not eat its own dog food for reindexing.
3756
            $this->assertEquals(1, $result->totalCount);
3757
3758
            $this->assertEquals(
3759
                $content->id,
3760
                $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3761
            );
3762
        } else {
3763
            // This is how it should eventually work for all search engines,
3764
            // with required reindexing slots properly implemented.
3765
3766
            $result = $searchService->findContent($query);
3767
3768
            // Assert last hit manually, as id will change because it is created in test
3769
            // and not present it base fixture.
3770
            $foundContent1 = array_pop($result->searchHits);
3771
            $result->totalCount = $result->totalCount - 1;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result->totalCount - 1 can also be of type double. However, the property $totalCount is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
3772
            $this->assertEquals($content->id, $foundContent1->valueObject->id);
3773
3774
            $this->simplifySearchResult($result);
3775
            $this->assertEquals(
3776
                include $this->getFixtureDir() . '/UserMetadata.php',
3777
                $result,
3778
                'Search results do not match.',
3779
                .1 // Be quite generous regarding delay -- most important for scores
3780
            );
3781
        }
3782
    }
3783
3784
    /**
3785
     * Test for the findContent() method.
3786
     *
3787
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3788
     */
3789
    public function testUserMetadataGroupHorizontalQueryContent()
3790
    {
3791
        $this->testUserMetadataGroupHorizontalFilterContent('query');
3792
    }
3793
3794
    /**
3795
     * Test for the findLocations() method.
3796
     *
3797
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3798
     */
3799
    public function testUserMetadataGroupHorizontalFilterLocation($queryType = null)
3800
    {
3801
        if ($queryType === null) {
3802
            $queryType = 'filter';
3803
        }
3804
3805
        $repository = $this->getRepository();
3806
        $searchService = $repository->getSearchService();
3807
        $editorsUserGroupId = 13;
3808
3809
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3810
3811
        $criteria = [];
3812
        $setupFactory = $this->getSetupFactory();
3813
3814
        // Do not limit for LSE, as it does not not require reindexing.
3815
        // See explanation below.
3816
        if ($setupFactory instanceof LegacySolrSetupFactory || $setupFactory instanceof LegacyElasticsearch) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3817
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3818
        }
3819
3820
        $criteria[] = new Criterion\UserMetadata(
3821
            Criterion\UserMetadata::GROUP,
3822
            Criterion\Operator::EQ,
3823
            $editorsUserGroupId
3824
        );
3825
3826
        $query = new LocationQuery(
3827
            [
3828
                $queryType => new Criterion\LogicalAnd($criteria),
3829
                'sortClauses' => [
3830
                    new SortClause\Location\Id(),
3831
                ],
3832
                'limit' => 50,
3833
            ]
3834
        );
3835
3836
        if ($setupFactory instanceof LegacySolrSetupFactory || $setupFactory instanceof LegacyElasticsearch) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
3837
            $result = $searchService->findLocations($query);
3838
3839
            // Administrator User is owned by itself, when additional Locations are added
3840
            // it should be reindexed and its UserGroups will updated, which means it should
3841
            // also be found as a Content of Editors UserGroup. However we do not handle this
3842
            // in slots yet, and also miss SPI methods to do it without using Search (also
3843
            // needed to decouple services), because as indexing is asynchronous Search
3844
            // should not eat its own dog food for reindexing.
3845
            $this->assertEquals(1, $result->totalCount);
3846
3847
            $this->assertEquals(
3848
                $content->contentInfo->mainLocationId,
3849
                $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3850
            );
3851
        } else {
3852
            // This is how it should eventually work for all search engines,
3853
            // with required reindexing slots properly implemented.
3854
3855
            $result = $searchService->findLocations($query);
3856
3857
            // Assert last two hits manually, as ids will change because they are created
3858
            // in test and not present in base fixture.
3859
            $foundLocation1 = array_pop($result->searchHits);
3860
            $foundLocation2 = array_pop($result->searchHits);
3861
            // Remove additional Administrators UserGroup Location
3862
            array_pop($result->searchHits);
3863
            $result->totalCount = $result->totalCount - 2;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result->totalCount - 2 can also be of type double. However, the property $totalCount is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
3864
            $this->assertEquals(
3865
                $content->versionInfo->contentInfo->mainLocationId,
3866
                $foundLocation1->valueObject->id
3867
            );
3868
            $this->assertEquals(
3869
                $repository->getCurrentUser()->id,
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\API\Repositor...itory::getCurrentUser() has been deprecated with message: since 6.6, to be removed. Use PermissionResolver::getCurrentUserReference() instead. Get current user. Loads the full user object if not already loaded, if you only need to know user id use {@see getCurrentUserReference()}

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3870
                $foundLocation2->valueObject->contentId
3871
            );
3872
3873
            $this->simplifySearchResult($result);
3874
            $this->assertEquals(
3875
                include $this->getFixtureDir() . '/UserMetadataLocation.php',
3876
                $result,
3877
                'Search results do not match.',
3878
                .1 // Be quite generous regarding delay -- most important for scores
3879
            );
3880
        }
3881
    }
3882
3883
    /**
3884
     * Test for the findLocations() method.
3885
     *
3886
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3887
     */
3888
    public function testUserMetadataGroupHorizontalQueryLocation()
3889
    {
3890
        $this->testUserMetadataGroupHorizontalFilterLocation('query');
3891
    }
3892
3893
    /**
3894
     * Test for FullText on the findContent() method.
3895
     *
3896
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3897
     */
3898
    public function testFullTextOnNewContent()
3899
    {
3900
        $repository = $this->getRepository();
3901
        $contentService = $repository->getContentService();
3902
        $contentTypeService = $repository->getContentTypeService();
3903
        $locationService = $repository->getLocationService();
3904
        $searchService = $repository->getSearchService();
3905
3906
        $contentCreateStruct = $contentService->newContentCreateStruct(
3907
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3908
            'eng-GB'
3909
        );
3910
3911
        $contentCreateStruct->setField('name', 'foxes');
3912
3913
        $englishContent = $contentService->publishVersion(
3914
            $contentService->createContent(
3915
                $contentCreateStruct,
3916
                [$locationService->newLocationCreateStruct(2)]
3917
            )->versionInfo
3918
        );
3919
3920
        $this->refreshSearch($repository);
3921
3922
        $query = new Query(
3923
            [
3924
                'query' => new Criterion\FullText('foxes'),
3925
            ]
3926
        );
3927
3928
        $searchResult = $searchService->findContentInfo($query);
3929
3930
        $this->assertEquals(1, $searchResult->totalCount);
3931
        $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3932
    }
3933
3934
    /**
3935
     * Test for the findContent() method.
3936
     *
3937
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3938
     */
3939
    public function testLanguageAnalysisSeparateContent()
3940
    {
3941
        $setupFactory = $this->getSetupFactory();
3942
        if (!$setupFactory instanceof LegacyElasticsearch) {
3943
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
3944
        }
3945
3946
        $repository = $this->getRepository();
3947
        $contentService = $repository->getContentService();
3948
        $contentTypeService = $repository->getContentTypeService();
3949
        $locationService = $repository->getLocationService();
3950
        $searchService = $repository->getSearchService();
3951
        $languageService = $repository->getContentLanguageService();
3952
3953
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
3954
        $languageCreateStruct->languageCode = 'rus-RU';
3955
        $languageCreateStruct->name = 'Russian';
3956
3957
        $languageService->createLanguage($languageCreateStruct);
3958
3959
        $contentCreateStruct = $contentService->newContentCreateStruct(
3960
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3961
            'eng-GB'
3962
        );
3963
3964
        $contentCreateStruct->setField('name', 'foxes');
3965
3966
        $englishContent = $contentService->publishVersion(
3967
            $contentService->createContent(
3968
                $contentCreateStruct,
3969
                [$locationService->newLocationCreateStruct(2)]
3970
            )->versionInfo
3971
        );
3972
3973
        $contentCreateStruct = $contentService->newContentCreateStruct(
3974
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3975
            'rus-RU'
3976
        );
3977
3978
        $contentCreateStruct->setField('name', 'foxes');
3979
3980
        $russianContent = $contentService->publishVersion(
0 ignored issues
show
Unused Code introduced by
$russianContent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3981
            $contentService->createContent(
3982
                $contentCreateStruct,
3983
                [$locationService->newLocationCreateStruct(2)]
3984
            )->versionInfo
3985
        );
3986
3987
        // Only Content in English should be found, because Content in Russian
3988
        // will not be correctly stemmed
3989
        $query = new Query(
3990
            [
3991
                'query' => new Criterion\FullText('foxing'),
3992
            ]
3993
        );
3994
3995
        $searchResult = $searchService->findContent($query);
3996
3997
        $this->assertEquals(1, $searchResult->totalCount);
3998
        $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3999
    }
4000
4001
    /**
4002
     * Test for the findContent() method.
4003
     *
4004
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4005
     */
4006
    public function testLanguageAnalysisSameContent()
4007
    {
4008
        $setupFactory = $this->getSetupFactory();
4009
        if (!$setupFactory instanceof LegacyElasticsearch) {
4010
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4011
        }
4012
4013
        $repository = $this->getRepository();
4014
        $contentService = $repository->getContentService();
4015
        $contentTypeService = $repository->getContentTypeService();
4016
        $locationService = $repository->getLocationService();
4017
        $searchService = $repository->getSearchService();
4018
        $languageService = $repository->getContentLanguageService();
4019
4020
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4021
        $languageCreateStruct->languageCode = 'rus-RU';
4022
        $languageCreateStruct->name = 'Russian';
4023
4024
        $languageService->createLanguage($languageCreateStruct);
4025
4026
        $contentCreateStruct = $contentService->newContentCreateStruct(
4027
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4028
            'eng-GB'
4029
        );
4030
4031
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4032
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4033
4034
        $mixedContent = $contentService->publishVersion(
4035
            $contentService->createContent(
4036
                $contentCreateStruct,
4037
                [$locationService->newLocationCreateStruct(2)]
4038
            )->versionInfo
4039
        );
4040
4041
        // Content will be found because translation in Russian will be correctly stemmed
4042
        $query = new Query(
4043
            [
4044
                'query' => new Criterion\FullText('важнее'),
4045
            ]
4046
        );
4047
4048
        $searchResult = $searchService->findContent($query);
4049
4050
        $this->assertEquals(1, $searchResult->totalCount);
4051
        $this->assertEquals($mixedContent->id, $searchResult->searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4052
    }
4053
4054
    /**
4055
     * Test for the findContent() method.
4056
     *
4057
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4058
     */
4059
    public function testLanguageAnalysisSameContentNotFound()
4060
    {
4061
        $setupFactory = $this->getSetupFactory();
4062
        if (!$setupFactory instanceof LegacyElasticsearch) {
4063
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4064
        }
4065
4066
        $repository = $this->getRepository();
4067
        $contentService = $repository->getContentService();
4068
        $contentTypeService = $repository->getContentTypeService();
4069
        $locationService = $repository->getLocationService();
4070
        $searchService = $repository->getSearchService();
4071
        $languageService = $repository->getContentLanguageService();
4072
4073
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4074
        $languageCreateStruct->languageCode = 'rus-RU';
4075
        $languageCreateStruct->name = 'Russian';
4076
4077
        $languageService->createLanguage($languageCreateStruct);
4078
4079
        $contentCreateStruct = $contentService->newContentCreateStruct(
4080
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4081
            'eng-GB'
4082
        );
4083
4084
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4085
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4086
4087
        $mixedContent = $contentService->publishVersion(
0 ignored issues
show
Unused Code introduced by
$mixedContent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4088
            $contentService->createContent(
4089
                $contentCreateStruct,
4090
                [$locationService->newLocationCreateStruct(2)]
4091
            )->versionInfo
4092
        );
4093
4094
        // Content should be found because translation in Russian will be correctly stemmed
4095
        $query = new Query(
4096
            [
4097
                'query' => new Criterion\FullText('важнее'),
4098
            ]
4099
        );
4100
4101
        // Filtering fields for only English will cause no match because the term will
4102
        // not be correctly stemmed
4103
        $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]);
4104
4105
        $this->assertEquals(0, $searchResult->totalCount);
4106
    }
4107
4108
    /**
4109
     * Test for the findContent() method searching for content filtered by languages.
4110
     *
4111
     * @covers \eZ\Publish\Core\Repository\SearchService::findContent
4112
     */
4113
    public function testFindContentWithLanguageFilter()
4114
    {
4115
        $repository = $this->getRepository();
4116
        $searchService = $repository->getSearchService();
4117
4118
        $query = new Query(
4119
            [
4120
                'filter' => new Criterion\ContentId([4]),
4121
                'offset' => 0,
4122
            ]
4123
        );
4124
        $searchResult = $searchService->findContent(
4125
            $query,
4126
            ['languages' => ['eng-US']],
4127
            false
4128
        );
4129
        /* END: Use Case */
4130
4131
        $this->assertInstanceOf(
4132
            SearchResult::class,
4133
            $searchResult
4134
        );
4135
4136
        $this->assertEquals(1, $searchResult->totalCount);
4137
        $this->assertCount($searchResult->totalCount, $searchResult->searchHits);
4138
        foreach ($searchResult->searchHits as $searchHit) {
4139
            $this->assertInstanceOf(
4140
                SearchHit::class,
4141
                $searchHit
4142
            );
4143
        }
4144
    }
4145
4146
    /**
4147
     * This test prepares data for other tests.
4148
     *
4149
     * @see testFulltextContentSearchComplex
4150
     * @see testFulltextLocationSearchComplex
4151
     *
4152
     * @return array
4153
     */
4154
    public function testFulltextComplex()
4155
    {
4156
        $repository = $this->getRepository();
4157
        $contentService = $repository->getContentService();
4158
        $contentTypeService = $repository->getContentTypeService();
4159
        $locationService = $repository->getLocationService();
4160
4161
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
4162
        $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4163
4164
        $contentCreateStruct->setField('name', 'red');
4165
        $contentCreateStruct->setField('short_name', 'red apple');
4166
        $content1 = $contentService->publishVersion(
4167
            $contentService->createContent(
4168
                $contentCreateStruct,
4169
                [$locationService->newLocationCreateStruct(2)]
4170
            )->versionInfo
4171
        );
4172
4173
        $contentCreateStruct->setField('name', 'apple');
4174
        $contentCreateStruct->setField('short_name', 'two');
4175
        $content2 = $contentService->publishVersion(
4176
            $contentService->createContent(
4177
                $contentCreateStruct,
4178
                [$locationService->newLocationCreateStruct(2)]
4179
            )->versionInfo
4180
        );
4181
4182
        $contentCreateStruct->setField('name', 'red apple');
4183
        $contentCreateStruct->setField('short_name', 'three');
4184
        $content3 = $contentService->publishVersion(
4185
            $contentService->createContent(
4186
                $contentCreateStruct,
4187
                [$locationService->newLocationCreateStruct(2)]
4188
            )->versionInfo
4189
        );
4190
4191
        $this->refreshSearch($repository);
4192
4193
        $criterion = new Criterion\FullText(
4194
            'red apple',
4195
            [
4196
                'boost' => [
4197
                    'short_name' => 2,
4198
                ],
4199
                'fuzziness' => .1,
4200
            ]
4201
        );
4202
4203
        return [$criterion, $content1, $content2, $content3];
4204
    }
4205
4206
    /**
4207
     * Test for the findContent() method.
4208
     *
4209
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4210
     * @depends testFulltextComplex
4211
     *
4212
     * @param array $data
4213
     */
4214
    public function testFulltextContentSearchComplex(array $data)
4215
    {
4216
        // Do not initialize from scratch
4217
        $repository = $this->getRepository(false);
4218
        $searchService = $repository->getSearchService();
4219
        list($criterion, $content1, $content2, $content3) = $data;
4220
4221
        $searchResult = $searchService->findContent(
4222
            new Query(['query' => $criterion]),
4223
            ['languages' => ['eng-GB']]
4224
        );
4225
        $searchHits = $searchResult->searchHits;
4226
4227
        $this->assertEquals(3, $searchResult->totalCount);
4228
4229
        // Legacy search engine does have scoring, sorting the results by ID in that case
4230
        $setupFactory = $this->getSetupFactory();
4231 View Code Duplication
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4232
            usort(
4233
                $searchHits,
4234
                function ($a, $b) {
4235
                    return ($a->valueObject->id < $b->valueObject->id) ? -1 : 1;
4236
                }
4237
            );
4238
4239
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
4240
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->id);
4241
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->id);
4242
4243
            return;
4244
        }
4245
4246
        // Assert scores are descending
4247
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4248
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4249
4250
        // Assert order
4251
        $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4252
        $this->assertEquals($content3->id, $searchHits[1]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4253
        $this->assertEquals($content2->id, $searchHits[2]->valueObject->id);
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4254
    }
4255
4256
    /**
4257
     * Test for the findLocations() method.
4258
     *
4259
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4260
     * @depends testFulltextComplex
4261
     *
4262
     * @param array $data
4263
     */
4264
    public function testFulltextLocationSearchComplex(array $data)
4265
    {
4266
        $setupFactory = $this->getSetupFactory();
4267
        if ($setupFactory instanceof LegacyElasticsearch) {
4268
            $this->markTestIncomplete(
4269
                'Fulltext criterion is not supported with Location search in Elasticsearch engine'
4270
            );
4271
        }
4272
4273
        if ($setupFactory instanceof LegacySolrSetupFactory && getenv('SOLR_VERSION') === '4.10.4') {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
4274
            $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!');
4275
        }
4276
4277
        // Do not initialize from scratch
4278
        $repository = $this->getRepository(false);
4279
        list($criterion, $content1, $content2, $content3) = $data;
4280
        $searchService = $repository->getSearchService();
4281
4282
        $searchResult = $searchService->findLocations(
4283
            new LocationQuery(['query' => $criterion]),
4284
            ['languages' => ['eng-GB']]
4285
        );
4286
        $searchHits = $searchResult->searchHits;
4287
4288
        $this->assertEquals(3, $searchResult->totalCount);
4289
4290
        // Legacy search engine does have scoring, sorting the results by ID in that case
4291
        $setupFactory = $this->getSetupFactory();
4292 View Code Duplication
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4293
            usort(
4294
                $searchHits,
4295
                function ($a, $b) {
4296
                    return ($a->valueObject->id < $b->valueObject->id) ? -1 : 1;
4297
                }
4298
            );
4299
4300
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
4301
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId);
4302
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId);
4303
4304
            return;
4305
        }
4306
4307
        // Assert scores are descending
4308
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4309
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4310
4311
        // Assert order
4312
        $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
0 ignored issues
show
Documentation introduced by
The property contentId does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4313
        $this->assertEquals($content3->id, $searchHits[1]->valueObject->contentId);
0 ignored issues
show
Documentation introduced by
The property contentId does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4314
        $this->assertEquals($content2->id, $searchHits[2]->valueObject->contentId);
0 ignored issues
show
Documentation introduced by
The property contentId does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
4315
    }
4316
4317
    /**
4318
     * Assert that query result matches the given fixture.
4319
     *
4320
     * @param Query $query
4321
     * @param string $fixtureFilePath
4322
     * @param null|callable $closure
4323
     * @param bool $ignoreScore
4324
     * @param bool $info
4325
     * @param bool $id
4326
     *
4327
     * @throws \ErrorException
4328
     * @throws \ReflectionException
4329
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
4330
     */
4331
    protected function assertQueryFixture(
4332
        Query $query,
4333
        $fixtureFilePath,
4334
        $closure = null,
4335
        $ignoreScore = true,
4336
        $info = false,
4337
        $id = true
4338
    ) {
4339
        $repository = $this->getRepository();
4340
        $searchService = $repository->getSearchService();
4341
4342
        try {
4343
            if ($query instanceof LocationQuery) {
4344
                $setupFactory = $this->getSetupFactory();
4345
                if ($setupFactory instanceof LegacySolrSetupFactory) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
4346
                    // @todo When we want to test score again by default we will need fixtures for Solr
4347
                }
4348
4349
                if ($setupFactory instanceof LegacyElasticsearch) {
4350
                    $position = strrpos($fixtureFilePath, '/');
4351
                    $fixtureFilePath = substr_replace($fixtureFilePath, '/Location', $position, 0);
4352
                }
4353
4354
                $result = $searchService->findLocations($query);
4355
            } elseif ($query instanceof Query) {
4356
                if ($info) {
4357
                    $result = $searchService->findContentInfo($query);
4358
                } else {
4359
                    $result = $searchService->findContent($query);
4360
                }
4361
            } else {
4362
                $this->fail('Expected instance of LocationQuery or Query, got: ' . gettype($query));
4363
            }
4364
            $this->simplifySearchResult($result);
0 ignored issues
show
Bug introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4365
        } catch (NotImplementedException $e) {
4366
            $this->markTestSkipped(
4367
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4368
            );
4369
        }
4370
4371 View Code Duplication
        if (!is_file($fixtureFilePath)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4372
            if (isset($_ENV['ez_tests_record'])) {
4373
                file_put_contents(
4374
                    $record = $fixtureFilePath . '.recording',
4375
                    "<?php\n\nreturn " . var_export($result, true) . ";\n\n"
4376
                );
4377
                $this->markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result));
4378
            } else {
4379
                $this->markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixtureFilePath);
4380
            }
4381
        }
4382
4383
        $fixture = require $fixtureFilePath;
4384
4385
        if ($closure !== null) {
4386
            $closure($fixture);
4387
            $closure($result);
4388
        }
4389
4390 View Code Duplication
        if ($ignoreScore) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4391
            foreach ([$fixture, $result] as $set) {
4392
                $property = new \ReflectionProperty(get_class($set), 'maxScore');
4393
                $property->setAccessible(true);
4394
                $property->setValue($set, 0.0);
4395
4396
                foreach ($set->searchHits as $hit) {
4397
                    $property = new \ReflectionProperty(get_class($hit), 'score');
4398
                    $property->setAccessible(true);
4399
                    $property->setValue($hit, 0.0);
4400
                }
4401
            }
4402
        }
4403
4404
        foreach ([$fixture, $result] as $set) {
4405
            foreach ($set->searchHits as $hit) {
4406
                $property = new \ReflectionProperty(get_class($hit), 'index');
4407
                $property->setAccessible(true);
4408
                $property->setValue($hit, null);
4409
4410
                $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation');
4411
                $property->setAccessible(true);
4412
                $property->setValue($hit, null);
4413
4414
                if (!$id) {
4415
                    $hit->valueObject['id'] = null;
4416
                }
4417
            }
4418
        }
4419
4420
        $this->assertEquals(
4421
            $fixture,
4422
            $result,
4423
            'Search results do not match the fixture: ' . $fixtureFilePath,
4424
            .99 // Be quite generous regarding delay -- most important for scores
4425
        );
4426
    }
4427
4428
    /**
4429
     * Show a simplified view of the search result for manual introspection.
4430
     *
4431
     * @param SearchResult $result
4432
     *
4433
     * @return string
4434
     */
4435 View Code Duplication
    protected function printResult(SearchResult $result)
4436
    {
4437
        $printed = '';
4438
        foreach ($result->searchHits as $hit) {
4439
            $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']);
4440
        }
4441
4442
        return $printed;
4443
    }
4444
4445
    /**
4446
     * Simplify search result.
4447
     *
4448
     * This leads to saner comparisons of results, since we do not get the full
4449
     * content objects every time.
4450
     *
4451
     * @param SearchResult $result
4452
     */
4453
    protected function simplifySearchResult(SearchResult $result)
4454
    {
4455
        $result->time = 1;
4456
4457
        foreach ($result->searchHits as $hit) {
4458
            switch (true) {
4459
                case $hit->valueObject instanceof Content:
4460 View Code Duplication
                case $hit->valueObject instanceof Location:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4461
                    $hit->valueObject = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('id' => $hit->valu...ect->contentInfo->name) of type array<string,?,{"id":"?","title":"?"}> is incompatible with the declared type object<eZ\Publish\API\Re...ory\Values\ValueObject> of property $valueObject.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4462
                        'id' => $hit->valueObject->contentInfo->id,
4463
                        'title' => $hit->valueObject->contentInfo->name,
4464
                    ];
4465
                    break;
4466
4467
                case $hit->valueObject instanceof ContentInfo:
4468
                    $hit->valueObject = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('id' => $hit->valu...hit->valueObject->name) of type array<string,*,{"id":"*","title":"string"}> is incompatible with the declared type object<eZ\Publish\API\Re...ory\Values\ValueObject> of property $valueObject.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4469
                        'id' => $hit->valueObject->id,
4470
                        'title' => $hit->valueObject->name,
4471
                    ];
4472
                    break;
4473
4474
                default:
4475
                    throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject));
4476
            }
4477
        }
4478
    }
4479
4480
    /**
4481
     * Get fixture directory.
4482
     *
4483
     * @return string
4484
     */
4485
    protected function getFixtureDir()
4486
    {
4487
        return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/';
4488
    }
4489
4490
    /**
4491
     * For findContentInfo tests, to reuse fixtures for findContent tests.
4492
     *
4493
     * @param null|callable $closure
4494
     *
4495
     * @return callable
4496
     */
4497
    private function getContentInfoFixtureClosure($closure = null)
4498
    {
4499
        /** @var $data \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */
4500
        return function (&$data) use ($closure) {
4501
            foreach ($data->searchHits as $searchHit) {
4502
                if ($searchHit->valueObject instanceof Content) {
4503
                    $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo();
4504
                }
4505
            }
4506
4507
            if (isset($closure)) {
4508
                $closure($data);
4509
            }
4510
        };
4511
    }
4512
4513
    /**
4514
     * @dataProvider providerForTestSortingByNumericFieldsWithValuesOfDifferentLength
4515
     *
4516
     * @param int[] $expectedOrderedIds
4517
     *
4518
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
4519
     */
4520
    public function testSortingByNumericFieldsWithValuesOfDifferentLength(
4521
        LocationQuery $query,
4522
        array $expectedOrderedIds
4523
    ) {
4524
        $repository = $this->getRepository();
4525
        $searchService = $repository->getSearchService();
4526
4527
        $result = $searchService->findLocations($query);
4528
4529
        self::assertEquals(count($expectedOrderedIds), $result->totalCount);
4530
        $actualIds = array_map(
4531
            static function (SearchHit $searchHit) {
4532
                /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
4533
                $location = $searchHit->valueObject;
4534
4535
                return $location->id;
4536
            },
4537
            $result->searchHits
4538
        );
4539
        self::assertEquals($expectedOrderedIds, $actualIds);
4540
    }
4541
4542
    public function providerForTestSortingByNumericFieldsWithValuesOfDifferentLength()
4543
    {
4544
        yield 'Location ID ASC' => [
4545
            new LocationQuery(
4546
                [
4547
                    'filter' => new Criterion\LocationId([43, 5]),
4548
                    'sortClauses' => [
4549
                        new SortClause\Location\Id(LocationQuery::SORT_ASC),
4550
                    ],
4551
                ]
4552
            ),
4553
            [5, 43],
4554
        ];
4555
4556
        yield 'Location ID DESC' => [
4557
            new LocationQuery(
4558
                [
4559
                    'filter' => new Criterion\LocationId([5, 43]),
4560
                    'sortClauses' => [
4561
                        new SortClause\Location\Id(LocationQuery::SORT_DESC),
4562
                    ],
4563
                ]
4564
            ),
4565
            [43, 5],
4566
        ];
4567
4568
        yield 'Content ID ASC' => [
4569
            new LocationQuery(
4570
                [
4571
                    'filter' => new Criterion\ContentId([14, 4]),
4572
                    'sortClauses' => [
4573
                        new SortClause\ContentId(LocationQuery::SORT_ASC),
4574
                    ],
4575
                ]
4576
            ),
4577
            // those are still Location IDs as it's LocationQuery
4578
            [5, 15],
4579
        ];
4580
4581
        yield 'Content ID DESC' => [
4582
            new LocationQuery(
4583
                [
4584
                    'filter' => new Criterion\ContentId([4, 14]),
4585
                    'sortClauses' => [
4586
                        new SortClause\ContentId(LocationQuery::SORT_DESC),
4587
                    ],
4588
                ]
4589
            ),
4590
            // those are still Location IDs as it's LocationQuery
4591
            [15, 5],
4592
        ];
4593
4594
        yield 'Content ID DESC' => [
4595
            new LocationQuery(
4596
                [
4597
                    'filter' => new Criterion\ContentId([4, 14]),
4598
                    'sortClauses' => [
4599
                        new SortClause\ContentId(LocationQuery::SORT_DESC),
4600
                    ],
4601
                ]
4602
            ),
4603
            // those are still Location IDs as it's LocationQuery
4604
            [15, 5],
4605
        ];
4606
    }
4607
}
4608