Completed
Push — EZP-31112-custom-aliases-gone-... ( 7e52f9...5caa2f )
by
unknown
18:51
created

SearchServiceTest::testFulltextComplex()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 62

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 62
rs 8.829
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 InvalidArgumentException;
14
use eZ\Publish\API\Repository\Values\Content\Content;
15
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
16
use eZ\Publish\API\Repository\Values\Content\Query;
17
use eZ\Publish\API\Repository\Values\Content\Location;
18
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
19
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
20
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
21
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
22
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
23
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
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
    const FIND_CONTENT_METHOD = 'findContent';
37
    const FIND_LOCATION_METHOD = 'findLocations';
38
39
    const AVAILABLE_FIND_METHODS = [
40
        self::FIND_CONTENT_METHOD,
41
        self::FIND_LOCATION_METHOD,
42
    ];
43
44
    use Common\FacetedSearchProvider;
45
46
    public function getFilterContentSearches()
47
    {
48
        $fixtureDir = $this->getFixtureDir();
49
50
        return [
51
            0 => [
52
                [
53
                    'filter' => new Criterion\ContentId(
54
                        [1, 4, 10]
55
                    ),
56
                    'sortClauses' => [new SortClause\ContentId()],
57
                ],
58
                $fixtureDir . 'ContentId.php',
59
            ],
60
            1 => [
61
                [
62
                    'filter' => new Criterion\LogicalAnd(
63
                        [
64
                            new Criterion\ContentId(
65
                                [1, 4, 10]
66
                            ),
67
                            new Criterion\ContentId(
68
                                [4, 12]
69
                            ),
70
                        ]
71
                    ),
72
                    'sortClauses' => [new SortClause\ContentId()],
73
                ],
74
                $fixtureDir . 'LogicalAnd.php',
75
            ],
76
            2 => [
77
                [
78
                    'filter' => new Criterion\LogicalOr(
79
                        [
80
                            new Criterion\ContentId(
81
                                [1, 4, 10]
82
                            ),
83
                            new Criterion\ContentId(
84
                                [4, 12]
85
                            ),
86
                        ]
87
                    ),
88
                    'sortClauses' => [new SortClause\ContentId()],
89
                ],
90
                $fixtureDir . 'LogicalOr.php',
91
            ],
92
            3 => [
93
                [
94
                    'filter' => new Criterion\LogicalAnd(
95
                        [
96
                            new Criterion\ContentId(
97
                                [1, 4, 10]
98
                            ),
99
                            new Criterion\LogicalNot(
100
                                new Criterion\ContentId(
101
                                    [10, 12]
102
                                )
103
                            ),
104
                        ]
105
                    ),
106
                    'sortClauses' => [new SortClause\ContentId()],
107
                ],
108
                $fixtureDir . 'LogicalNot.php',
109
            ],
110
            4 => [
111
                [
112
                    'filter' => new Criterion\LogicalAnd(
113
                        [
114
                            new Criterion\ContentId(
115
                                [1, 4, 10]
116
                            ),
117
                            new Criterion\LogicalAnd(
118
                                [
119
                                    new Criterion\LogicalNot(
120
                                        new Criterion\ContentId(
121
                                            [10, 12]
122
                                        )
123
                                    ),
124
                                ]
125
                            ),
126
                        ]
127
                    ),
128
                    'sortClauses' => [new SortClause\ContentId()],
129
                ],
130
                $fixtureDir . 'LogicalNot.php',
131
            ],
132
            5 => [
133
                [
134
                    'filter' => new Criterion\ContentTypeId(
135
                        4
136
                    ),
137
                    'sortClauses' => [new SortClause\ContentId()],
138
                ],
139
                $fixtureDir . 'ContentTypeId.php',
140
            ],
141
            6 => [
142
                [
143
                    'filter' => new Criterion\ContentTypeIdentifier(
144
                        'user'
145
                    ),
146
                    'sortClauses' => [new SortClause\ContentId()],
147
                ],
148
                $fixtureDir . 'ContentTypeId.php',
149
            ],
150
            7 => [
151
                [
152
                    'filter' => new Criterion\ContentTypeIdentifier(
153
                        'user',
154
                        'invalid'
155
                    ),
156
                    'sortClauses' => [new SortClause\ContentId()],
157
                ],
158
                $fixtureDir . 'ContentTypeId.php',
159
            ],
160
            8 => [
161
                [
162
                    'filter' => new Criterion\ContentTypeIdentifier(
163
                        'invalid1',
164
                        'invalid2'
165
                    ),
166
                    'sortClauses' => [new SortClause\ContentId()],
167
                ],
168
                $fixtureDir . 'MatchNone.php',
169
            ],
170
            9 => [
171
                [
172
                    'filter' => new Criterion\MatchNone(),
173
                    'sortClauses' => [new SortClause\ContentId()],
174
                ],
175
                $fixtureDir . 'MatchNone.php',
176
            ],
177
            10 => [
178
                [
179
                    'filter' => new Criterion\ContentTypeGroupId(
180
                        2
181
                    ),
182
                    'sortClauses' => [new SortClause\ContentId()],
183
                ],
184
                $fixtureDir . 'ContentTypeGroupId.php',
185
            ],
186
            11 => [
187
                [
188
                    'filter' => new Criterion\DateMetadata(
189
                        Criterion\DateMetadata::MODIFIED,
190
                        Criterion\Operator::GT,
191
                        1343140540
192
                    ),
193
                    'sortClauses' => [new SortClause\ContentId()],
194
                ],
195
                $fixtureDir . 'DateMetadataGt.php',
196
            ],
197
            12 => [
198
                [
199
                    'filter' => new Criterion\DateMetadata(
200
                        Criterion\DateMetadata::MODIFIED,
201
                        Criterion\Operator::GTE,
202
                        1311154215
203
                    ),
204
                    'sortClauses' => [new SortClause\ContentId()],
205
                ],
206
                $fixtureDir . 'DateMetadataGte.php',
207
            ],
208
            13 => [
209
                [
210
                    'filter' => new Criterion\DateMetadata(
211
                        Criterion\DateMetadata::MODIFIED,
212
                        Criterion\Operator::LTE,
213
                        1311154215
214
                    ),
215
                    'limit' => 10,
216
                    'sortClauses' => [new SortClause\ContentId()],
217
                ],
218
                $fixtureDir . 'DateMetadataLte.php',
219
            ],
220
            14 => [
221
                [
222
                    'filter' => new Criterion\DateMetadata(
223
                        Criterion\DateMetadata::MODIFIED,
224
                        Criterion\Operator::IN,
225
                        [1033920794, 1060695457, 1343140540]
226
                    ),
227
                    'sortClauses' => [new SortClause\ContentId()],
228
                ],
229
                $fixtureDir . 'DateMetadataIn.php',
230
            ],
231
            15 => [
232
                [
233
                    'filter' => new Criterion\DateMetadata(
234
                        Criterion\DateMetadata::MODIFIED,
235
                        Criterion\Operator::BETWEEN,
236
                        [1033920776, 1072180276]
237
                    ),
238
                    'sortClauses' => [new SortClause\ContentId()],
239
                ],
240
                $fixtureDir . 'DateMetadataBetween.php',
241
            ],
242
            16 => [
243
                [
244
                    'filter' => new Criterion\DateMetadata(
245
                        Criterion\DateMetadata::CREATED,
246
                        Criterion\Operator::BETWEEN,
247
                        [1033920776, 1072180278]
248
                    ),
249
                    'sortClauses' => [new SortClause\ContentId()],
250
                ],
251
                $fixtureDir . 'DateMetadataCreated.php',
252
            ],
253
            17 => [
254
                [
255
                    'filter' => new Criterion\CustomField(
256
                        'user_group_name_value_s',
257
                        Criterion\Operator::EQ,
258
                        'Members'
259
                    ),
260
                    'sortClauses' => [new SortClause\ContentId()],
261
                ],
262
                $fixtureDir . 'Field.php',
263
            ],
264
            18 => [
265
                [
266
                    'filter' => new Criterion\CustomField(
267
                        'user_group_name_value_s',
268
                        Criterion\Operator::CONTAINS,
269
                        'Members'
270
                    ),
271
                    'sortClauses' => [new SortClause\ContentId()],
272
                ],
273
                $fixtureDir . 'Field.php',
274
            ],
275
            19 => [
276
                [
277
                    'filter' => new Criterion\CustomField(
278
                        'user_group_name_value_s',
279
                        Criterion\Operator::LT,
280
                        'Members'
281
                    ),
282
                    'sortClauses' => [new SortClause\ContentId()],
283
                ],
284
                $fixtureDir . 'CustomFieldLt.php',
285
            ],
286
            20 => [
287
                [
288
                    'filter' => new Criterion\CustomField(
289
                        'user_group_name_value_s',
290
                        Criterion\Operator::LTE,
291
                        'Members'
292
                    ),
293
                    'sortClauses' => [new SortClause\ContentId()],
294
                ],
295
                $fixtureDir . 'CustomFieldLte.php',
296
            ],
297
            21 => [
298
                [
299
                    'filter' => new Criterion\CustomField(
300
                        'user_group_name_value_s',
301
                        Criterion\Operator::GT,
302
                        'Members'
303
                    ),
304
                    'sortClauses' => [new SortClause\ContentId()],
305
                ],
306
                $fixtureDir . 'CustomFieldGt.php',
307
            ],
308
            22 => [
309
                [
310
                    'filter' => new Criterion\CustomField(
311
                        'user_group_name_value_s',
312
                        Criterion\Operator::GTE,
313
                        'Members'
314
                    ),
315
                    'sortClauses' => [new SortClause\ContentId()],
316
                ],
317
                $fixtureDir . 'CustomFieldGte.php',
318
            ],
319
            23 => [
320
                [
321
                    'filter' => new Criterion\CustomField(
322
                        'user_group_name_value_s',
323
                        Criterion\Operator::BETWEEN,
324
                        ['Administrator users', 'Members']
325
                    ),
326
                    'sortClauses' => [new SortClause\ContentId()],
327
                ],
328
                $fixtureDir . 'CustomFieldBetween.php',
329
            ],
330
            24 => [
331
                [
332
                    'filter' => new Criterion\RemoteId(
333
                        ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca']
334
                    ),
335
                    'sortClauses' => [new SortClause\ContentId()],
336
                ],
337
                $fixtureDir . 'RemoteId.php',
338
            ],
339
            25 => [
340
                [
341
                    'filter' => new Criterion\SectionId(
342
                        [2]
343
                    ),
344
                    'sortClauses' => [new SortClause\ContentId()],
345
                ],
346
                $fixtureDir . 'SectionId.php',
347
            ],
348
            26 => [
349
                [
350
                    'filter' => new Criterion\Field(
351
                        'name',
352
                        Criterion\Operator::EQ,
353
                        'Members'
354
                    ),
355
                    'sortClauses' => [new SortClause\ContentId()],
356
                ],
357
                $fixtureDir . 'Field.php',
358
            ],
359
            27 => [
360
                [
361
                    'filter' => new Criterion\Field(
362
                        'name',
363
                        Criterion\Operator::IN,
364
                        ['Members', 'Anonymous Users']
365
                    ),
366
                    'sortClauses' => [new SortClause\ContentId()],
367
                ],
368
                $fixtureDir . 'FieldIn.php',
369
            ],
370
            28 => [
371
                [
372
                    'filter' => new Criterion\DateMetadata(
373
                        Criterion\DateMetadata::MODIFIED,
374
                        Criterion\Operator::BETWEEN,
375
                        [1033920275, 1033920794]
376
                    ),
377
                    'sortClauses' => [new SortClause\ContentId()],
378
                ],
379
                $fixtureDir . 'FieldBetween.php',
380
            ],
381
            29 => [
382
                [
383
                    'filter' => new Criterion\LogicalOr(
384
                        [
385
                            new Criterion\Field(
386
                                'name',
387
                                Criterion\Operator::EQ,
388
                                'Members'
389
                            ),
390
                            new Criterion\DateMetadata(
391
                                Criterion\DateMetadata::MODIFIED,
392
                                Criterion\Operator::BETWEEN,
393
                                [1033920275, 1033920794]
394
                            ),
395
                        ]
396
                    ),
397
                    'sortClauses' => [new SortClause\ContentId()],
398
                ],
399
                $fixtureDir . 'FieldOr.php',
400
            ],
401
            30 => [
402
                [
403
                    'filter' => new Criterion\Subtree(
404
                        '/1/5/'
405
                    ),
406
                    'sortClauses' => [new SortClause\ContentId()],
407
                ],
408
                $fixtureDir . 'Subtree.php',
409
            ],
410
            31 => [
411
                [
412
                    'filter' => new Criterion\LocationId(
413
                        [1, 2, 5]
414
                    ),
415
                    'sortClauses' => [new SortClause\ContentId()],
416
                ],
417
                $fixtureDir . 'LocationId.php',
418
            ],
419
            32 => [
420
                [
421
                    'filter' => new Criterion\ParentLocationId(
422
                        [1]
423
                    ),
424
                    'sortClauses' => [new SortClause\ContentId()],
425
                ],
426
                $fixtureDir . 'ParentLocationId.php',
427
            ],
428
            33 => [
429
                [
430
                    'filter' => new Criterion\LocationRemoteId(
431
                        ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983']
432
                    ),
433
                    'sortClauses' => [new SortClause\ContentId()],
434
                ],
435
                $fixtureDir . 'LocationRemoteId.php',
436
            ],
437
            34 => [
438
                [
439
                    // There is no Status Criterion anymore, this should match all published as well
440
                    'filter' => new Criterion\Subtree(
441
                        '/1/'
442
                    ),
443
                    'sortClauses' => [new SortClause\ContentId()],
444
                    'limit' => 50,
445
                ],
446
                $fixtureDir . 'Status.php',
447
                // Result having the same sort level should be sorted between them to be system independent
448
                function (&$data) {
449
                    usort(
450
                        $data->searchHits,
451
                        function ($a, $b) {
452
                            if ($a->score == $b->score) {
453
                                if ($a->valueObject['id'] == $b->valueObject['id']) {
454
                                    return 0;
455
                                }
456
457
                                // Order by ascending ID
458
                                return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
459
                            }
460
461
                            // Order by descending score
462
                            return ($a->score > $b->score) ? -1 : 1;
463
                        }
464
                    );
465
                },
466
            ],
467
            35 => [
468
                [
469
                    'filter' => new Criterion\UserMetadata(
470
                        Criterion\UserMetadata::MODIFIER,
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::MODIFIER,
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::OWNER,
499
                        Criterion\Operator::EQ,
500
                        14
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::OWNER,
513
                        Criterion\Operator::IN,
514
                        [14]
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
                        12
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
                        [12]
543
                    ),
544
                    'sortClauses' => [
545
                        new SortClause\ContentId(),
546
                    ],
547
                    'limit' => 50,
548
                ],
549
                $fixtureDir . 'UserMetadata.php',
550
            ],
551
            41 => [
552
                [
553
                    'filter' => new Criterion\UserMetadata(
554
                        Criterion\UserMetadata::GROUP,
555
                        Criterion\Operator::EQ,
556
                        4
557
                    ),
558
                    'sortClauses' => [
559
                        new SortClause\ContentId(),
560
                    ],
561
                    'limit' => 50,
562
                ],
563
                $fixtureDir . 'UserMetadata.php',
564
            ],
565
            42 => [
566
                [
567
                    'filter' => new Criterion\UserMetadata(
568
                        Criterion\UserMetadata::GROUP,
569
                        Criterion\Operator::IN,
570
                        [4]
571
                    ),
572
                    'sortClauses' => [
573
                        new SortClause\ContentId(),
574
                    ],
575
                    'limit' => 50,
576
                ],
577
                $fixtureDir . 'UserMetadata.php',
578
            ],
579
            43 => [
580
                [
581
                    'filter' => new Criterion\Ancestor(
582
                        [
583
                            '/1/5/44/',
584
                            '/1/5/44/45/',
585
                        ]
586
                    ),
587
                    'sortClauses' => [
588
                        new SortClause\ContentId(),
589
                    ],
590
                    'limit' => 50,
591
                ],
592
                $fixtureDir . 'AncestorContent.php',
593
            ],
594
        ];
595
    }
596
597
    public function getContentQuerySearches()
598
    {
599
        $fixtureDir = $this->getFixtureDir();
600
601
        return [
602
            [
603
                [
604
                    'filter' => new Criterion\ContentId(
605
                        [58, 10]
606
                    ),
607
                    'query' => new Criterion\FullText('contact'),
608
                    'sortClauses' => [new SortClause\ContentId()],
609
                ],
610
                $fixtureDir . 'FullTextFiltered.php',
611
            ],
612
            [
613
                [
614
                    'query' => new Criterion\FullText(
615
                        'contact',
616
                        [
617
                            'boost' => [
618
                                'title' => 2,
619
                            ],
620
                            'fuzziness' => .5,
621
                        ]
622
                    ),
623
                    'sortClauses' => [new SortClause\ContentId()],
624
                ],
625
                $fixtureDir . 'FullText.php',
626
            ],
627
            [
628
                [
629
                    'query' => new Criterion\FullText(
630
                        'Contact*'
631
                    ),
632
                    'sortClauses' => [new SortClause\ContentId()],
633
                ],
634
                $fixtureDir . 'FullTextWildcard.php',
635
            ],
636
            [
637
                [
638
                    'query' => new Criterion\LanguageCode('eng-GB', false),
639
                    'sortClauses' => [new SortClause\ContentId()],
640
                ],
641
                $fixtureDir . 'LanguageCode.php',
642
            ],
643
            [
644
                [
645
                    'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']),
646
                    'offset' => 10,
647
                    'sortClauses' => [new SortClause\ContentId()],
648
                ],
649
                $fixtureDir . 'LanguageCodeIn.php',
650
            ],
651
            [
652
                [
653
                    'query' => new Criterion\LanguageCode('eng-GB'),
654
                    'offset' => 10,
655
                    'sortClauses' => [new SortClause\ContentId()],
656
                ],
657
                $fixtureDir . 'LanguageCodeAlwaysAvailable.php',
658
            ],
659
            [
660
                [
661
                    'query' => new Criterion\Visibility(
662
                        Criterion\Visibility::VISIBLE
663
                    ),
664
                    'sortClauses' => [new SortClause\ContentId()],
665
                    'limit' => 50,
666
                ],
667
                $fixtureDir . 'Visibility.php',
668
            ],
669
        ];
670
    }
671
672
    public function getLocationQuerySearches()
673
    {
674
        $fixtureDir = $this->getFixtureDir();
675
676
        return [
677
            [
678
                [
679
                    'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1),
680
                    'sortClauses' => [new SortClause\ContentId()],
681
                ],
682
                $fixtureDir . 'Depth.php',
683
            ],
684
            [
685
                [
686
                    'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]),
687
                    'sortClauses' => [new SortClause\ContentId()],
688
                ],
689
                $fixtureDir . 'DepthIn.php',
690
            ],
691
            [
692
                [
693
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2),
694
                    'sortClauses' => [new SortClause\ContentId()],
695
                ],
696
                $fixtureDir . 'DepthGt.php',
697
            ],
698
            [
699
                [
700
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2),
701
                    'sortClauses' => [new SortClause\ContentId()],
702
                    'limit' => 50,
703
                ],
704
                $fixtureDir . 'DepthGte.php',
705
            ],
706
            [
707
                [
708
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2),
709
                    'sortClauses' => [new SortClause\ContentId()],
710
                ],
711
                $fixtureDir . 'Depth.php',
712
            ],
713
            [
714
                [
715
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2),
716
                    'sortClauses' => [new SortClause\ContentId()],
717
                    'limit' => 50,
718
                ],
719
                $fixtureDir . 'DepthLte.php',
720
            ],
721
            [
722
                [
723
                    'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]),
724
                    'sortClauses' => [new SortClause\ContentId()],
725
                    'limit' => 50,
726
                ],
727
                $fixtureDir . 'DepthLte.php',
728
            ],
729
            [
730
                [
731
                    'filter' => new Criterion\Ancestor('/1/5/44/45/'),
732
                    'sortClauses' => [
733
                        new SortClause\Location\Depth(),
734
                    ],
735
                    'limit' => 50,
736
                ],
737
                $fixtureDir . 'AncestorLocation.php',
738
            ],
739
        ];
740
    }
741
742
    /**
743
     * Test for the findContent() method.
744
     *
745
     * @dataProvider getFilterContentSearches
746
     *
747
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
748
     */
749
    public function testFindContentFiltered($queryData, $fixture, $closure = null)
750
    {
751
        $query = new Query($queryData);
752
        $this->assertQueryFixture($query, $fixture, $closure);
753
    }
754
755
    /**
756
     * Test for the findContentInfo() method.
757
     *
758
     * @dataProvider getFilterContentSearches
759
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
760
     */
761
    public function testFindContentInfoFiltered($queryData, $fixture, $closure = null)
762
    {
763
        $query = new Query($queryData);
764
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
765
    }
766
767
    /**
768
     * Test for the findLocations() method.
769
     *
770
     * @dataProvider getFilterContentSearches
771
     *
772
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
773
     */
774
    public function testFindLocationsContentFiltered($queryData, $fixture, $closure = null)
775
    {
776
        $query = new LocationQuery($queryData);
777
        $this->assertQueryFixture($query, $fixture, $closure);
778
    }
779
780
    /**
781
     * Test for deprecated $criterion property on query object.
782
     *
783
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
784
     * @deprecated
785
     */
786
    public function testDeprecatedCriteriaProperty()
787
    {
788
        $this->assertQueryFixture(
789
            new Query(
790
                [
791
                    'query' => new Criterion\ContentId(
792
                        [1, 4, 10]
793
                    ),
794
                    'sortClauses' => [new SortClause\ContentId()],
795
                ]
796
            ),
797
            $this->getFixtureDir() . 'DeprecatedContentIdQuery.php'
798
        );
799
    }
800
801
    /**
802
     * Test for the findContent() method.
803
     *
804
     * @dataProvider getContentQuerySearches
805
     *
806
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
807
     */
808
    public function testQueryContent($queryData, $fixture, $closure = null)
809
    {
810
        $query = new Query($queryData);
811
        $this->assertQueryFixture($query, $fixture, $closure);
812
    }
813
814
    /**
815
     * Test for the findContentInfo() method.
816
     *
817
     * @dataProvider getContentQuerySearches
818
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
819
     */
820
    public function testQueryContentInfo($queryData, $fixture, $closure = null)
821
    {
822
        $query = new Query($queryData);
823
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
824
    }
825
826
    /**
827
     * Test for the findLocations() method.
828
     *
829
     * @dataProvider getContentQuerySearches
830
     *
831
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
832
     */
833
    public function testQueryContentLocations($queryData, $fixture, $closure = null)
834
    {
835
        $query = new LocationQuery($queryData);
836
        $this->assertQueryFixture($query, $fixture, $closure);
837
    }
838
839
    /**
840
     * Test for the findLocations() method.
841
     *
842
     * @dataProvider getLocationQuerySearches
843
     *
844
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
845
     */
846
    public function testQueryLocations($queryData, $fixture, $closure = null)
847
    {
848
        $query = new LocationQuery($queryData);
849
        $this->assertQueryFixture($query, $fixture, $closure);
850
    }
851
852
    public function getCaseInsensitiveSearches()
853
    {
854
        return [
855
            [
856
                [
857
                    'filter' => new Criterion\Field(
858
                        'name',
859
                        Criterion\Operator::EQ,
860
                        'Members'
861
                    ),
862
                    'sortClauses' => [new SortClause\ContentId()],
863
                ],
864
            ],
865
            [
866
                [
867
                    'filter' => new Criterion\Field(
868
                        'name',
869
                        Criterion\Operator::EQ,
870
                        'members'
871
                    ),
872
                    'sortClauses' => [new SortClause\ContentId()],
873
                ],
874
            ],
875
            [
876
                [
877
                    'filter' => new Criterion\Field(
878
                        'name',
879
                        Criterion\Operator::EQ,
880
                        'MEMBERS'
881
                    ),
882
                    'sortClauses' => [new SortClause\ContentId()],
883
                ],
884
            ],
885
        ];
886
    }
887
888
    /**
889
     * Test for the findContent() method.
890
     *
891
     * @dataProvider getCaseInsensitiveSearches
892
     *
893
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
894
     */
895
    public function testFindContentFieldFiltersCaseSensitivity($queryData)
896
    {
897
        $query = new Query($queryData);
898
        $this->assertQueryFixture(
899
            $query,
900
            $this->getFixtureDir() . 'Field.php'
901
        );
902
    }
903
904
    /**
905
     * Test for the findLocations() method.
906
     *
907
     * @dataProvider getCaseInsensitiveSearches
908
     *
909
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
910
     */
911
    public function testFindLocationsFieldFiltersCaseSensitivity($queryData)
912
    {
913
        $query = new LocationQuery($queryData);
914
        $this->assertQueryFixture(
915
            $query,
916
            $this->getFixtureDir() . 'Field.php'
917
        );
918
    }
919
920
    public function getRelationFieldFilterSearches()
921
    {
922
        $fixtureDir = $this->getFixtureDir();
923
924
        return [
925
            0 => [
926
                [
927
                    'filter' => new Criterion\FieldRelation(
928
                        'image',
929
                        Criterion\Operator::IN,
930
                        [1, 4, 10]
931
                    ),
932
                    'sortClauses' => [new SortClause\ContentId()],
933
                ],
934
                $fixtureDir . 'FieldRelation.php',
935
            ],
936
            1 => [
937
                [
938
                    'filter' => new Criterion\FieldRelation(
939
                        'image',
940
                        Criterion\Operator::IN,
941
                        [4, 49]
942
                    ),
943
                    'sortClauses' => [new SortClause\ContentId()],
944
                ],
945
                $fixtureDir . 'FieldRelationAll.php',
946
            ],
947
            2 => [
948
                [
949
                    'filter' => new Criterion\FieldRelation(
950
                        'image',
951
                        Criterion\Operator::IN,
952
                        [4]
953
                    ),
954
                    'sortClauses' => [new SortClause\ContentId()],
955
                ],
956
                $fixtureDir . 'FieldRelation.php',
957
            ],
958
            3 => [
959
                [
960
                    'filter' => new Criterion\FieldRelation(
961
                        'image',
962
                        Criterion\Operator::CONTAINS,
963
                        [1, 4, 10]
964
                    ),
965
                    'sortClauses' => [new SortClause\ContentId()],
966
                ],
967
                $fixtureDir . 'MatchNone.php',
968
            ],
969
            4 => [
970
                [
971
                    'filter' => new Criterion\FieldRelation(
972
                        'image',
973
                        Criterion\Operator::CONTAINS,
974
                        [4, 49]
975
                    ),
976
                    'sortClauses' => [new SortClause\ContentId()],
977
                ],
978
                $fixtureDir . 'MatchNone.php',
979
            ],
980
            5 => [
981
                [
982
                    'filter' => new Criterion\FieldRelation(
983
                        'image',
984
                        Criterion\Operator::CONTAINS,
985
                        [4]
986
                    ),
987
                    'sortClauses' => [new SortClause\ContentId()],
988
                ],
989
                $fixtureDir . 'FieldRelation.php',
990
            ],
991
        ];
992
    }
993
994
    /**
995
     * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered()
996
     * and testFindRelationFieldLocationsFiltered().
997
     */
998
    public function testRelationContentCreation()
999
    {
1000
        $repository = $this->getRepository();
1001
        $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery');
1002
        $contentService = $repository->getContentService();
1003
        $locationService = $repository->getLocationService();
1004
1005
        $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home
1006
1007
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
1008
        $createStruct->setField('name', 'Image gallery');
1009
        $createStruct->setField('image', 49); // Images folder
1010
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1011
        $contentService->publishVersion($draft->getVersionInfo());
1012
1013
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
1014
        $createStruct->setField('name', 'User gallery');
1015
        $createStruct->setField('image', 4); // User folder
1016
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1017
        $contentService->publishVersion($draft->getVersionInfo());
1018
1019
        $this->refreshSearch($repository);
1020
    }
1021
1022
    /**
1023
     * Test for FieldRelation using findContentInfo() method.
1024
     *
1025
     * @dataProvider getRelationFieldFilterSearches
1026
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
1027
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1028
     */
1029
    public function testFindRelationFieldContentInfoFiltered($queryData, $fixture)
1030
    {
1031
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1032
        $query = new Query($queryData);
1033
        $this->assertQueryFixture($query, $fixture, null, true, true, false);
1034
    }
1035
1036
    /**
1037
     * Test for FieldRelation using findLocations() method.
1038
     *
1039
     * @dataProvider getRelationFieldFilterSearches
1040
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
1041
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1042
     */
1043
    public function testFindRelationFieldLocationsFiltered($queryData, $fixture)
1044
    {
1045
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1046
        $query = new LocationQuery($queryData);
1047
        $this->assertQueryFixture($query, $fixture, null, true, false, false);
1048
    }
1049
1050 View Code Duplication
    public function testFindSingle()
1051
    {
1052
        $repository = $this->getRepository();
1053
        $searchService = $repository->getSearchService();
1054
1055
        $content = $searchService->findSingle(
1056
            new Criterion\ContentId(
1057
                [4]
1058
            )
1059
        );
1060
1061
        $this->assertEquals(
1062
            4,
1063
            $content->id
1064
        );
1065
    }
1066
1067 View Code Duplication
    public function testFindNoPerformCount()
1068
    {
1069
        $repository = $this->getRepository();
1070
        $searchService = $repository->getSearchService();
1071
1072
        $query = new Query();
1073
        $query->performCount = false;
1074
        $query->query = new Criterion\ContentTypeId(
1075
            [4]
1076
        );
1077
1078
        $searchHit = $searchService->findContent($query);
1079
1080
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1081
            $this->assertNull(
1082
                $searchHit->totalCount
1083
            );
1084
        } else {
1085
            $this->assertEquals(
1086
                2,
1087
                $searchHit->totalCount
1088
            );
1089
        }
1090
    }
1091
1092
    /**
1093
     * @expectedException \RuntimeException
1094
     */
1095 View Code Duplication
    public function testFindNoPerformCountException()
1096
    {
1097
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1098
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1099
        }
1100
1101
        $repository = $this->getRepository();
1102
        $searchService = $repository->getSearchService();
1103
1104
        $query = new Query();
1105
        $query->performCount = false;
1106
        $query->limit = 0;
1107
        $query->query = new Criterion\ContentTypeId(
1108
            [4]
1109
        );
1110
1111
        $searchService->findContent($query);
1112
    }
1113
1114 View Code Duplication
    public function testFindLocationsNoPerformCount()
1115
    {
1116
        $repository = $this->getRepository();
1117
        $searchService = $repository->getSearchService();
1118
1119
        $query = new LocationQuery();
1120
        $query->performCount = false;
1121
        $query->query = new Criterion\ContentTypeId(
1122
            [4]
1123
        );
1124
1125
        $searchHit = $searchService->findLocations($query);
1126
1127
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1128
            $this->assertNull(
1129
                $searchHit->totalCount
1130
            );
1131
        } else {
1132
            $this->assertEquals(
1133
                2,
1134
                $searchHit->totalCount
1135
            );
1136
        }
1137
    }
1138
1139
    /**
1140
     * @expectedException \RuntimeException
1141
     */
1142 View Code Duplication
    public function testFindLocationsNoPerformCountException()
1143
    {
1144
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1145
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1146
        }
1147
1148
        $repository = $this->getRepository();
1149
        $searchService = $repository->getSearchService();
1150
1151
        $query = new LocationQuery();
1152
        $query->performCount = false;
1153
        $query->limit = 0;
1154
        $query->query = new Criterion\ContentTypeId(
1155
            [4]
1156
        );
1157
1158
        $searchService->findLocations($query);
1159
    }
1160
1161
    /**
1162
     * Create movie Content with subtitle field set to null.
1163
     *
1164
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1165
     */
1166
    protected function createMovieContent()
1167
    {
1168
        $movies = [];
1169
1170
        $repository = $this->getRepository();
1171
        $contentTypeService = $repository->getContentTypeService();
1172
        $contentService = $repository->getContentService();
1173
1174
        $createStruct = $contentTypeService->newContentTypeCreateStruct('movie');
1175
        $createStruct->mainLanguageCode = 'eng-GB';
1176
        $createStruct->remoteId = 'movie-123';
1177
        $createStruct->names = ['eng-GB' => 'Movie'];
1178
        $createStruct->creatorId = 14;
1179
        $createStruct->creationDate = new \DateTime();
1180
1181
        $fieldTitle = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring');
1182
        $fieldTitle->names = ['eng-GB' => 'Title'];
1183
        $fieldTitle->fieldGroup = 'main';
1184
        $fieldTitle->position = 1;
1185
        $fieldTitle->isTranslatable = false;
1186
        $fieldTitle->isSearchable = true;
1187
        $fieldTitle->isRequired = true;
1188
        $createStruct->addFieldDefinition($fieldTitle);
1189
1190
        $fieldSubtitle = $contentTypeService->newFieldDefinitionCreateStruct('subtitle', 'ezstring');
1191
        $fieldSubtitle->names = ['eng-GB' => 'Subtitle'];
1192
        $fieldSubtitle->fieldGroup = 'main';
1193
        $fieldSubtitle->position = 2;
1194
        $fieldSubtitle->isTranslatable = false;
1195
        $fieldSubtitle->isSearchable = true;
1196
        $fieldSubtitle->isRequired = false;
1197
        $createStruct->addFieldDefinition($fieldSubtitle);
1198
1199
        $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1200
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentTypeGroup]);
1201
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1202
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1203
1204
        $createStructRambo = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1205
        $createStructRambo->remoteId = 'movie-456';
1206
        $createStructRambo->alwaysAvailable = false;
1207
        $createStructRambo->setField('title', 'Rambo');
1208
1209
        $ramboDraft = $contentService->createContent($createStructRambo);
1210
        $movies[] = $contentService->publishVersion($ramboDraft->getVersionInfo());
1211
        $this->refreshSearch($repository);
1212
        $createStructRobocop = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1213
        $createStructRobocop->remoteId = 'movie-789';
1214
        $createStructRobocop->alwaysAvailable = false;
1215
        $createStructRobocop->setField('title', 'Robocop');
1216
        $createStructRobocop->setField('subtitle', '');
1217
1218
        $robocopDraft = $contentService->createContent($createStructRobocop);
1219
        $movies[] = $contentService->publishVersion($robocopDraft->getVersionInfo());
1220
        $this->refreshSearch($repository);
1221
        $createStructLastHope = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1222
        $createStructLastHope->remoteId = 'movie-101112';
1223
        $createStructLastHope->alwaysAvailable = false;
1224
        $createStructLastHope->setField('title', 'Star Wars');
1225
        $createStructLastHope->setField('subtitle', 'Last Hope');
1226
1227
        $lastHopeDraft = $contentService->createContent($createStructLastHope);
1228
        $movies[] = $contentService->publishVersion($lastHopeDraft->getVersionInfo());
1229
1230
        $this->refreshSearch($repository);
1231
1232
        return $movies;
1233
    }
1234
1235
    /**
1236
     * Create test Content with ezcountry field having multiple countries selected.
1237
     *
1238
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1239
     */
1240
    protected function createMultipleCountriesContent()
1241
    {
1242
        $repository = $this->getRepository();
1243
        $contentTypeService = $repository->getContentTypeService();
1244
        $contentService = $repository->getContentService();
1245
1246
        $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple');
1247
        $createStruct->mainLanguageCode = 'eng-GB';
1248
        $createStruct->remoteId = 'countries-multiple-123';
1249
        $createStruct->names = ['eng-GB' => 'Multiple countries'];
1250
        $createStruct->creatorId = 14;
1251
        $createStruct->creationDate = new \DateTime();
1252
1253
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry');
1254
        $fieldCreate->names = ['eng-GB' => 'Countries'];
1255
        $fieldCreate->fieldGroup = 'main';
1256
        $fieldCreate->position = 1;
1257
        $fieldCreate->isTranslatable = false;
1258
        $fieldCreate->isSearchable = true;
1259
        $fieldCreate->fieldSettings = ['isMultiple' => true];
1260
1261
        $createStruct->addFieldDefinition($fieldCreate);
1262
1263
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1264
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1265
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1266
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1267
1268
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1269
        $createStruct->remoteId = 'countries-multiple-456';
1270
        $createStruct->alwaysAvailable = false;
1271
        $createStruct->setField(
1272
            'countries',
1273
            ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU']
1274
        );
1275
1276
        $draft = $contentService->createContent($createStruct);
1277
        $content = $contentService->publishVersion($draft->getVersionInfo());
1278
1279
        $this->refreshSearch($repository);
1280
1281
        return $content;
1282
    }
1283
1284
    /**
1285
     * Test for the findContent() method.
1286
     *
1287
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1288
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1289
     */
1290 View Code Duplication
    public function testFieldIsEmpty()
1291
    {
1292
        $testContents = $this->createMovieContent();
1293
1294
        $query = new Query(
1295
            [
1296
                'query' => new Criterion\IsFieldEmpty('subtitle'),
1297
            ]
1298
        );
1299
1300
        $repository = $this->getRepository();
1301
        $searchService = $repository->getSearchService();
1302
        $result = $searchService->findContent($query, ['eng-GB']);
1303
1304
        $this->assertEquals(2, $result->totalCount);
1305
1306
        $this->assertEquals(
1307
            $testContents[0]->id,
1308
            $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...
1309
        );
1310
        $this->assertEquals(
1311
            $testContents[1]->id,
1312
            $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...
1313
        );
1314
1315
        return $testContents;
1316
    }
1317
1318
    /**
1319
     * Test for the findContent() method.
1320
     *
1321
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1322
     */
1323 View Code Duplication
    public function testFieldIsNotEmpty()
1324
    {
1325
        $testContents = $this->createMovieContent();
1326
1327
        $query = new Query(
1328
            [
1329
                'query' => new Criterion\IsFieldEmpty(
1330
                    'subtitle',
1331
                    false
1332
                ),
1333
            ]
1334
        );
1335
1336
        $repository = $this->getRepository();
1337
        $searchService = $repository->getSearchService();
1338
        $result = $searchService->findContent($query, ['eng-GB']);
1339
1340
        $this->assertEquals(1, $result->totalCount);
1341
        $this->assertEquals(
1342
            $testContents[2]->id,
1343
            $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...
1344
        );
1345
    }
1346
1347
    /**
1348
     * Test for the findContent() method.
1349
     *
1350
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1351
     */
1352 View Code Duplication
    public function testFieldCollectionContains()
1353
    {
1354
        $testContent = $this->createMultipleCountriesContent();
1355
1356
        $query = new Query(
1357
            [
1358
                'query' => new Criterion\Field(
1359
                    'countries',
1360
                    Criterion\Operator::CONTAINS,
1361
                    'Belgium'
1362
                ),
1363
            ]
1364
        );
1365
1366
        $repository = $this->getRepository();
1367
        $searchService = $repository->getSearchService();
1368
        $result = $searchService->findContent($query);
1369
1370
        $this->assertEquals(1, $result->totalCount);
1371
        $this->assertEquals(
1372
            $testContent->id,
1373
            $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...
1374
        );
1375
    }
1376
1377
    /**
1378
     * Test for the findContent() method.
1379
     *
1380
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1381
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains
1382
     */
1383 View Code Duplication
    public function testFieldCollectionContainsNoMatch()
1384
    {
1385
        $this->createMultipleCountriesContent();
1386
        $query = new Query(
1387
            [
1388
                'query' => new Criterion\Field(
1389
                    'countries',
1390
                    Criterion\Operator::CONTAINS,
1391
                    'Netherlands Antilles'
1392
                ),
1393
            ]
1394
        );
1395
1396
        $repository = $this->getRepository();
1397
        $searchService = $repository->getSearchService();
1398
        $result = $searchService->findContent($query);
1399
1400
        $this->assertEquals(0, $result->totalCount);
1401
    }
1402
1403
    /**
1404
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1405
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1406
     */
1407 View Code Duplication
    public function testInvalidFieldIdentifierRange()
1408
    {
1409
        $repository = $this->getRepository();
1410
        $searchService = $repository->getSearchService();
1411
1412
        $searchService->findContent(
1413
            new Query(
1414
                [
1415
                    'filter' => new Criterion\Field(
1416
                        'some_hopefully_unknown_field',
1417
                        Criterion\Operator::BETWEEN,
1418
                        [10, 1000]
1419
                    ),
1420
                    'sortClauses' => [new SortClause\ContentId()],
1421
                ]
1422
            )
1423
        );
1424
    }
1425
1426
    /**
1427
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1428
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1429
     */
1430 View Code Duplication
    public function testInvalidFieldIdentifierIn()
1431
    {
1432
        $repository = $this->getRepository();
1433
        $searchService = $repository->getSearchService();
1434
1435
        $searchService->findContent(
1436
            new Query(
1437
                [
1438
                    'filter' => new Criterion\Field(
1439
                        'some_hopefully_unknown_field',
1440
                        Criterion\Operator::EQ,
1441
                        1000
1442
                    ),
1443
                    'sortClauses' => [new SortClause\ContentId()],
1444
                ]
1445
            )
1446
        );
1447
    }
1448
1449
    /**
1450
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1451
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'tag_cloud_url'
1452
     */
1453 View Code Duplication
    public function testFindContentWithNonSearchableField()
1454
    {
1455
        $repository = $this->getRepository();
1456
        $searchService = $repository->getSearchService();
1457
1458
        $searchService->findContent(
1459
            new Query(
1460
                [
1461
                    'filter' => new Criterion\Field(
1462
                        'tag_cloud_url',
1463
                        Criterion\Operator::EQ,
1464
                        'http://nimbus.com'
1465
                    ),
1466
                    'sortClauses' => [new SortClause\ContentId()],
1467
                ]
1468
            )
1469
        );
1470
    }
1471
1472
    /**
1473
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1474
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1475
     */
1476 View Code Duplication
    public function testSortFieldWithNonSearchableField()
1477
    {
1478
        $repository = $this->getRepository();
1479
        $searchService = $repository->getSearchService();
1480
1481
        $searchService->findContent(
1482
            new Query(
1483
                [
1484
                    'sortClauses' => [new SortClause\Field('template_look', 'title')],
1485
                ]
1486
            )
1487
        );
1488
    }
1489
1490
    /**
1491
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1492
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1493
     */
1494 View Code Duplication
    public function testSortMapLocationDistanceWithNonSearchableField()
1495
    {
1496
        $repository = $this->getRepository();
1497
        $searchService = $repository->getSearchService();
1498
1499
        $searchService->findContent(
1500
            new Query(
1501
                [
1502
                    'sortClauses' => [
1503
                        new SortClause\MapLocationDistance(
1504
                            'template_look',
1505
                            'title',
1506
                            1,
1507
                            2
1508
                        ),
1509
                    ],
1510
                ]
1511
            )
1512
        );
1513
    }
1514
1515
    /**
1516
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1517
     */
1518 View Code Duplication
    public function testFindSingleFailMultiple()
1519
    {
1520
        $repository = $this->getRepository();
1521
        $searchService = $repository->getSearchService();
1522
1523
        $searchService->findSingle(
1524
            new Criterion\ContentId(
1525
                [4, 10]
1526
            )
1527
        );
1528
    }
1529
1530
    /**
1531
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1532
     */
1533
    public function testFindSingleWithNonSearchableField()
1534
    {
1535
        $repository = $this->getRepository();
1536
        $searchService = $repository->getSearchService();
1537
1538
        $searchService->findSingle(
1539
            new Criterion\Field(
1540
                'tag_cloud_url',
1541
                Criterion\Operator::EQ,
1542
                'http://nimbus.com'
1543
            )
1544
        );
1545
    }
1546
1547
    public function getSortedContentSearches()
1548
    {
1549
        $fixtureDir = $this->getFixtureDir();
1550
1551
        return [
1552
            0 => [
1553
                [
1554
                    'filter' => new Criterion\SectionId([2]),
1555
                    'offset' => 0,
1556
                    'limit' => 10,
1557
                    'sortClauses' => [],
1558
                ],
1559
                $fixtureDir . 'SortNone.php',
1560
                // Result having the same sort level should be sorted between them to be system independent
1561
                function (&$data) {
1562
                    usort(
1563
                        $data->searchHits,
1564
                        function ($a, $b) {
1565
                            return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
1566
                        }
1567
                    );
1568
                },
1569
            ],
1570
            1 => [
1571
                [
1572
                    'filter' => new Criterion\SectionId([2]),
1573
                    'offset' => 0,
1574
                    'limit' => 10,
1575
                    'sortClauses' => [
1576
                        new SortClause\DatePublished(),
1577
                        new SortClause\ContentId(),
1578
                    ],
1579
                ],
1580
                $fixtureDir . 'SortDatePublished.php',
1581
            ],
1582
            2 => [
1583
                [
1584
                    'filter' => new Criterion\SectionId([2]),
1585
                    'offset' => 0,
1586
                    'limit' => 50,
1587
                    'sortClauses' => [
1588
                        new SortClause\DateModified(),
1589
                        new SortClause\ContentId(),
1590
                    ],
1591
                ],
1592
                $fixtureDir . 'SortDateModified.php',
1593
            ],
1594
            3 => [
1595
                [
1596
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1597
                    'offset' => 0,
1598
                    'limit' => 50,
1599
                    'sortClauses' => [
1600
                        new SortClause\SectionIdentifier(),
1601
                        new SortClause\ContentId(),
1602
                    ],
1603
                ],
1604
                $fixtureDir . 'SortSectionIdentifier.php',
1605
            ],
1606
            4 => [
1607
                [
1608
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1609
                    'offset' => 0,
1610
                    'limit' => 50,
1611
                    'sortClauses' => [
1612
                        new SortClause\SectionName(),
1613
                        new SortClause\ContentId(),
1614
                    ],
1615
                ],
1616
                $fixtureDir . 'SortSectionName.php',
1617
            ],
1618
            5 => [
1619
                [
1620
                    'filter' => new Criterion\SectionId([2, 3]),
1621
                    'offset' => 0,
1622
                    'limit' => 50,
1623
                    'sortClauses' => [
1624
                        new SortClause\ContentName(),
1625
                        new SortClause\ContentId(),
1626
                    ],
1627
                ],
1628
                $fixtureDir . 'SortContentName.php',
1629
            ],
1630
            6 => [
1631
                [
1632
                    'filter' => new Criterion\ContentTypeId(1),
1633
                    'offset' => 0,
1634
                    'limit' => 50,
1635
                    'sortClauses' => [
1636
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1637
                        new SortClause\ContentId(),
1638
                    ],
1639
                ],
1640
                $fixtureDir . 'SortFolderName.php',
1641
            ],
1642
            7 => [
1643
                [
1644
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1645
                    'offset' => 0,
1646
                    'limit' => 50,
1647
                    'sortClauses' => [
1648
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1649
                        new SortClause\ContentId(),
1650
                    ],
1651
                ],
1652
                $fixtureDir . 'SortFieldMultipleTypes.php',
1653
            ],
1654
            8 => [
1655
                [
1656
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1657
                    'offset' => 0,
1658
                    'limit' => 50,
1659
                    'sortClauses' => [
1660
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1661
                        new SortClause\ContentId(),
1662
                    ],
1663
                ],
1664
                $fixtureDir . 'SortFieldMultipleTypesReverse.php',
1665
            ],
1666
            9 => [
1667
                [
1668
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1669
                    'offset' => 3,
1670
                    'limit' => 5,
1671
                    'sortClauses' => [
1672
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1673
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1674
                        new SortClause\ContentId(),
1675
                    ],
1676
                ],
1677
                $fixtureDir . 'SortFieldMultipleTypesSlice.php',
1678
            ],
1679
            10 => [
1680
                [
1681
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1682
                    'offset' => 3,
1683
                    'limit' => 5,
1684
                    'sortClauses' => [
1685
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1686
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1687
                        new SortClause\ContentId(),
1688
                    ],
1689
                ],
1690
                $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php',
1691
            ],
1692
        ];
1693
    }
1694
1695
    public function getSortedLocationSearches()
1696
    {
1697
        $fixtureDir = $this->getFixtureDir();
1698
1699
        return [
1700
            [
1701
                [
1702
                    'filter' => new Criterion\SectionId([2]),
1703
                    'offset' => 0,
1704
                    'limit' => 10,
1705
                    'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)],
1706
                ],
1707
                $fixtureDir . 'SortPathString.php',
1708
            ],
1709
            [
1710
                [
1711
                    'filter' => new Criterion\SectionId([2]),
1712
                    'offset' => 0,
1713
                    'limit' => 10,
1714
                    'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)],
1715
                ],
1716
                $fixtureDir . 'SortLocationDepth.php',
1717
                // Result having the same sort level should be sorted between them to be system independent
1718
                function (&$data) {
1719
                    // Result with ids:
1720
                    //     4 has depth = 1
1721
                    //     11, 12, 13, 42, 59 have depth = 2
1722
                    //     10, 14 have depth = 3
1723
                    $map = [
1724
                        4 => 0,
1725
                        11 => 1,
1726
                        12 => 2,
1727
                        13 => 3,
1728
                        42 => 4,
1729
                        59 => 5,
1730
                        10 => 6,
1731
                        14 => 7,
1732
                    ];
1733
                    usort(
1734
                        $data->searchHits,
1735
                        function ($a, $b) use ($map) {
1736
                            return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1;
1737
                        }
1738
                    );
1739
                },
1740
            ],
1741
            [
1742
                [
1743
                    'filter' => new Criterion\SectionId([3]),
1744
                    'offset' => 0,
1745
                    'limit' => 10,
1746
                    'sortClauses' => [
1747
                        new SortClause\Location\Path(Query::SORT_DESC),
1748
                        new SortClause\ContentName(Query::SORT_ASC),
1749
                    ],
1750
                ],
1751
                $fixtureDir . 'SortMultiple.php',
1752
            ],
1753
            [
1754
                [
1755
                    'filter' => new Criterion\SectionId([2]),
1756
                    'offset' => 0,
1757
                    'limit' => 10,
1758
                    'sortClauses' => [
1759
                        new SortClause\Location\Priority(Query::SORT_DESC),
1760
                        new SortClause\ContentId(),
1761
                    ],
1762
                ],
1763
                $fixtureDir . 'SortDesc.php',
1764
            ],
1765
        ];
1766
    }
1767
1768
    /**
1769
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
1770
     */
1771 View Code Duplication
    protected function createTestContentType()
1772
    {
1773
        $repository = $this->getRepository();
1774
        $contentTypeService = $repository->getContentTypeService();
1775
1776
        $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type');
1777
        $createStruct->mainLanguageCode = 'eng-GB';
1778
        $createStruct->names = ['eng-GB' => 'Test type'];
1779
        $createStruct->creatorId = 14;
1780
        $createStruct->creationDate = new \DateTime();
1781
1782
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger');
1783
        $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field'];
1784
        $translatableFieldCreate->fieldGroup = 'main';
1785
        $translatableFieldCreate->position = 1;
1786
        $translatableFieldCreate->isTranslatable = true;
1787
        $translatableFieldCreate->isSearchable = true;
1788
1789
        $createStruct->addFieldDefinition($translatableFieldCreate);
1790
1791
        $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger');
1792
        $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field'];
1793
        $nonTranslatableFieldCreate->fieldGroup = 'main';
1794
        $nonTranslatableFieldCreate->position = 2;
1795
        $nonTranslatableFieldCreate->isTranslatable = false;
1796
        $nonTranslatableFieldCreate->isSearchable = true;
1797
1798
        $createStruct->addFieldDefinition($nonTranslatableFieldCreate);
1799
1800
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1801
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1802
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1803
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1804
1805
        return $contentType;
1806
    }
1807
1808
    /**
1809
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1810
     * @param int $fieldValue11 Value for translatable field in first language
1811
     * @param int $fieldValue12 Value for translatable field in second language
1812
     * @param int $fieldValue2 Value for non translatable field
1813
     * @param string $mainLanguageCode
1814
     * @param bool $alwaysAvailable
1815
     *
1816
     * @return Content
1817
     */
1818
    protected function createMultilingualContent(
1819
        $contentType,
1820
        $fieldValue11 = null,
1821
        $fieldValue12 = null,
1822
        $fieldValue2 = null,
1823
        $mainLanguageCode = 'eng-GB',
1824
        $alwaysAvailable = false
1825
    ) {
1826
        $repository = $this->getRepository();
1827
        $contentService = $repository->getContentService();
1828
1829
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1830
        $createStruct->alwaysAvailable = $alwaysAvailable;
1831
        $createStruct->mainLanguageCode = $mainLanguageCode;
1832
        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...
1833
            $createStruct->setField('integer', $fieldValue11, 'eng-GB');
1834
        }
1835
        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...
1836
            $createStruct->setField('integer', $fieldValue12, 'ger-DE');
1837
        }
1838
        $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode);
1839
1840
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
1841
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1842
        $content = $contentService->publishVersion($draft->getVersionInfo());
1843
1844
        $this->refreshSearch($repository);
1845
1846
        return $content;
1847
    }
1848
1849
    protected function checkPrioritizedLanguagesSupport()
1850
    {
1851
        $setupFactory = $this->getSetupFactory();
1852
        if ($setupFactory instanceof LegacyElasticsearch) {
1853
            $this->markTestIncomplete('Prioritized languages are not supported with Elasticsearch engine');
1854
        }
1855
    }
1856
1857
    public function providerForTestMultilingualFieldSort()
1858
    {
1859
        return [
1860
            0 => [
1861
                [
1862
                    1 => [1, 2, 1],
1863
                    2 => [2, 1, 2],
1864
                    3 => [2, 1, 3],
1865
                    4 => [1, 2, 4],
1866
                ],
1867
                [
1868
                    'languages' => [
1869
                        'eng-GB',
1870
                        'ger-DE',
1871
                    ],
1872
                ],
1873
                [
1874
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1875
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1876
                ],
1877
                /**
1878
                 * Expected order, Value eng-GB, Value ger-DE.
1879
                 *
1880
                 * Content 4, 1, 2, 4
1881
                 * Content 1, 1, 2, 1
1882
                 * Content 3, 2, 1, 3
1883
                 * Content 2, 2, 1, 2
1884
                 */
1885
                [4, 1, 3, 2],
1886
            ],
1887
            1 => [
1888
                [
1889
                    1 => [1, 2, 1],
1890
                    2 => [2, 1, 2],
1891
                    3 => [2, 1, 3],
1892
                    4 => [1, 2, 4],
1893
                ],
1894
                [
1895
                    'languages' => [
1896
                        'ger-DE',
1897
                        'eng-GB',
1898
                    ],
1899
                ],
1900
                [
1901
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1902
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1903
                ],
1904
                /**
1905
                 * Expected order, Value eng-GB, Value ger-DE.
1906
                 *
1907
                 * Content 3, 2, 1, 3
1908
                 * Content 2, 2, 1, 2
1909
                 * Content 4, 1, 2, 4
1910
                 * Content 1, 1, 2, 1
1911
                 */
1912
                [3, 2, 4, 1],
1913
            ],
1914
            2 => [
1915
                [
1916
                    1 => [null, 2, null, 'ger-DE'],
1917
                    2 => [3, null, null, 'eng-GB'],
1918
                    3 => [4, null, null, 'eng-GB'],
1919
                    4 => [null, 1, null, 'ger-DE'],
1920
                ],
1921
                [
1922
                    'languages' => [
1923
                        'eng-GB',
1924
                    ],
1925
                ],
1926
                [
1927
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1928
                ],
1929
                /**
1930
                 * Expected order, Value eng-GB, Value ger-DE.
1931
                 *
1932
                 * Content 3, 4, -
1933
                 * Content 2, 3, -
1934
                 */
1935
                [3, 2],
1936
            ],
1937
            3 => [
1938
                [
1939
                    1 => [null, 2, null, 'ger-DE'],
1940
                    2 => [3, null, null, 'eng-GB'],
1941
                    3 => [4, null, null, 'eng-GB'],
1942
                    4 => [null, 1, null, 'ger-DE'],
1943
                ],
1944
                [
1945
                    'languages' => [
1946
                        'ger-DE',
1947
                    ],
1948
                ],
1949
                [
1950
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1951
                ],
1952
                /**
1953
                 * Expected order, Value eng-GB, Value ger-DE.
1954
                 *
1955
                 * Content 1, -, 2
1956
                 * Content 4, -, 1
1957
                 */
1958
                [1, 4],
1959
            ],
1960
            4 => [
1961
                [
1962
                    1 => [null, 2, null, 'ger-DE'],
1963
                    2 => [3, null, null, 'eng-GB'],
1964
                    3 => [4, null, null, 'eng-GB'],
1965
                    4 => [null, 1, null, 'ger-DE'],
1966
                ],
1967
                [
1968
                    'languages' => [
1969
                        'eng-GB',
1970
                        'ger-DE',
1971
                    ],
1972
                ],
1973
                [
1974
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1975
                ],
1976
                /**
1977
                 * Expected order, Value eng-GB, Value ger-DE.
1978
                 *
1979
                 * Content 3, 4, -
1980
                 * Content 2, 3, -
1981
                 * Content 1, -, 2
1982
                 * Content 4, -, 1
1983
                 */
1984
                [3, 2, 1, 4],
1985
            ],
1986
            5 => [
1987
                [
1988
                    1 => [null, 2, null, 'ger-DE'],
1989
                    2 => [3, null, null, 'eng-GB'],
1990
                    3 => [4, null, null, 'eng-GB'],
1991
                    4 => [null, 1, null, 'ger-DE'],
1992
                ],
1993
                [
1994
                    'languages' => [
1995
                        'ger-DE',
1996
                        'eng-GB',
1997
                    ],
1998
                ],
1999
                [
2000
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2001
                ],
2002
                /**
2003
                 * Expected order, Value eng-GB, Value ger-DE.
2004
                 *
2005
                 * Content 3, 4, -
2006
                 * Content 2, 3, -
2007
                 * Content 1, -, 2
2008
                 * Content 4, -, 1
2009
                 */
2010
                [3, 2, 1, 4],
2011
            ],
2012
            6 => [
2013
                [
2014
                    1 => [null, 2, null, 'ger-DE'],
2015
                    2 => [3, 4, null, 'eng-GB'],
2016
                    3 => [4, 3, null, 'eng-GB'],
2017
                    4 => [null, 1, null, 'ger-DE'],
2018
                ],
2019
                [
2020
                    'languages' => [
2021
                        'eng-GB',
2022
                        'ger-DE',
2023
                    ],
2024
                ],
2025
                [
2026
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2027
                ],
2028
                /**
2029
                 * Expected order, Value eng-GB, Value ger-DE.
2030
                 *
2031
                 * Content 3, 4, 3
2032
                 * Content 2, 3, 4
2033
                 * Content 1, -, 2
2034
                 * Content 4, -, 1
2035
                 */
2036
                [3, 2, 1, 4],
2037
            ],
2038
            7 => [
2039
                [
2040
                    1 => [null, 2, null, 'ger-DE'],
2041
                    2 => [3, 4, null, 'eng-GB'],
2042
                    3 => [4, 3, null, 'eng-GB'],
2043
                    4 => [null, 1, null, 'ger-DE'],
2044
                ],
2045
                [
2046
                    'languages' => [
2047
                        'ger-DE',
2048
                        'eng-GB',
2049
                    ],
2050
                ],
2051
                [
2052
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2053
                ],
2054
                /**
2055
                 * Expected order, Value eng-GB, Value ger-DE.
2056
                 *
2057
                 * Content 2, 3, 4
2058
                 * Content 3, 4, 3
2059
                 * Content 1, -, 2
2060
                 * Content 4, -, 1
2061
                 */
2062
                [2, 3, 1, 4],
2063
            ],
2064
            8 => [
2065
                [
2066
                    1 => [null, 1, null, 'ger-DE', true],
2067
                    2 => [4, null, null, 'eng-GB', true],
2068
                    3 => [3, null, null, 'eng-GB', false],
2069
                    4 => [null, 2, null, 'ger-DE', false],
2070
                ],
2071
                [
2072
                    'languages' => [
2073
                        'eng-GB',
2074
                    ],
2075
                ],
2076
                [
2077
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
2078
                ],
2079
                /**
2080
                 * Expected order, Value eng-GB, Value ger-DE.
2081
                 *
2082
                 * Content 1, -, 1
2083
                 * Content 3, 3, -
2084
                 * Content 2, 4, -
2085
                 */
2086
                [1, 3, 2],
2087
            ],
2088
            9 => [
2089
                [
2090
                    1 => [null, 1, null, 'ger-DE', true],
2091
                    2 => [4, null, null, 'eng-GB', true],
2092
                    3 => [3, null, null, 'eng-GB', false],
2093
                    4 => [null, 2, null, 'ger-DE', false],
2094
                ],
2095
                [
2096
                    'languages' => [
2097
                        'ger-DE',
2098
                    ],
2099
                ],
2100
                [
2101
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2102
                ],
2103
                /**
2104
                 * Expected order, Value eng-GB, Value ger-DE.
2105
                 *
2106
                 * Content 2, 4, -
2107
                 * Content 4, -, 2
2108
                 * Content 1, -, 1
2109
                 */
2110
                [2, 4, 1],
2111
            ],
2112
            10 => [
2113
                [
2114
                    1 => [null, 1, null, 'ger-DE', true],
2115
                    2 => [4, null, null, 'eng-GB', true],
2116
                    3 => [3, null, null, 'eng-GB', false],
2117
                    4 => [null, 2, null, 'ger-DE', false],
2118
                ],
2119
                [
2120
                    'languages' => [
2121
                        'eng-GB',
2122
                    ],
2123
                    'useAlwaysAvailable' => false,
2124
                ],
2125
                [
2126
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
2127
                ],
2128
                /**
2129
                 * Expected order, Value eng-GB, Value ger-DE.
2130
                 *
2131
                 * Content 3, 3, -
2132
                 * Content 2, 4, -
2133
                 */
2134
                [3, 2],
2135
            ],
2136
            11 => [
2137
                [
2138
                    1 => [null, 1, null, 'ger-DE', true],
2139
                    2 => [4, null, null, 'eng-GB', true],
2140
                    3 => [3, null, null, 'eng-GB', false],
2141
                    4 => [null, 2, null, 'ger-DE', false],
2142
                ],
2143
                [
2144
                    'languages' => [
2145
                        'ger-DE',
2146
                    ],
2147
                    'useAlwaysAvailable' => false,
2148
                ],
2149
                [
2150
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2151
                ],
2152
                /**
2153
                 * Expected order, Value eng-GB, Value ger-DE.
2154
                 *
2155
                 * Content 4, -, 2
2156
                 * Content 1, -, 1
2157
                 */
2158
                [4, 1],
2159
            ],
2160
        ];
2161
    }
2162
2163
    /**
2164
     * Test for the findContent() method.
2165
     *
2166
     * @group rrr
2167
     * @dataProvider providerForTestMultilingualFieldSort
2168
     *
2169
     * @param array $contentDataList
2170
     * @param array $languageSettings
2171
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2172
     * @param array $expected
2173
     */
2174
    public function testMultilingualFieldSortContent(
2175
        array $contentDataList,
2176
        $languageSettings,
2177
        array $sortClauses,
2178
        $expected
2179
    ) {
2180
        $this->assertMultilingualFieldSort(
2181
            $contentDataList,
2182
            $languageSettings,
2183
            $sortClauses,
2184
            $expected
2185
        );
2186
    }
2187
2188
    /**
2189
     * Test for the findLocations() method.
2190
     *
2191
     * @group rrr
2192
     * @dataProvider providerForTestMultilingualFieldSort
2193
     *
2194
     * @param array $contentDataList
2195
     * @param array $languageSettings
2196
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2197
     * @param array $expected
2198
     */
2199
    public function testMultilingualFieldSortLocation(
2200
        array $contentDataList,
2201
        $languageSettings,
2202
        array $sortClauses,
2203
        $expected
2204
    ) {
2205
        $this->assertMultilingualFieldSort(
2206
            $contentDataList,
2207
            $languageSettings,
2208
            $sortClauses,
2209
            $expected,
2210
            false
2211
        );
2212
    }
2213
2214
    /**
2215
     * @param array $contentDataList
2216
     * @param array $languageSettings
2217
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2218
     * @param array $expected
2219
     * @param bool $contentSearch
2220
     */
2221
    protected function assertMultilingualFieldSort(
2222
        array $contentDataList,
2223
        $languageSettings,
2224
        array $sortClauses,
2225
        $expected,
2226
        $contentSearch = true
2227
    ) {
2228
        $this->checkPrioritizedLanguagesSupport();
2229
        $contentType = $this->createTestContentType();
2230
2231
        // Create a draft to account for behaviour with ContentType in different states
2232
        $repository = $this->getRepository();
2233
        $contentTypeService = $repository->getContentTypeService();
2234
        $contentTypeService->createContentTypeDraft($contentType);
2235
2236
        $defaults = [null, null, null, 'eng-GB', false];
2237
        $contentIdList = [];
2238 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...
2239
            $contentData = $contentData + $defaults;
2240
            list(
2241
                $fieldValue11,
2242
                $fieldValue12,
2243
                $fieldValue2,
2244
                $mainLanguageCode,
2245
                $alwaysAvailable
2246
            ) = $contentData;
2247
2248
            $contentIdList[$key] = $this->createMultilingualContent(
2249
                $contentType,
2250
                $fieldValue11,
2251
                $fieldValue12,
2252
                $fieldValue2,
2253
                $mainLanguageCode,
2254
                $alwaysAvailable
2255
            )->id;
2256
        }
2257
2258
        // "article" type Content is not matched, this ensures that non-matched
2259
        // field does not affect sort
2260
        $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC);
2261
        array_unshift($sortClauses, $dummySortClause);
2262
        $sortClauses[] = $dummySortClause;
2263
2264
        $searchService = $repository->getSearchService();
2265
        if ($contentSearch) {
2266
            $query = new Query(
2267
                [
2268
                    'query' => new Criterion\ContentTypeId($contentType->id),
2269
                    'sortClauses' => $sortClauses,
2270
                ]
2271
            );
2272
            $result = $searchService->findContent($query, $languageSettings);
2273
        } else {
2274
            $query = new LocationQuery(
2275
                [
2276
                    'query' => new Criterion\ContentTypeId($contentType->id),
2277
                    'sortClauses' => $sortClauses,
2278
                ]
2279
            );
2280
            $result = $searchService->findLocations($query, $languageSettings);
2281
        }
2282
2283
        $this->assertEquals(count($expected), $result->totalCount);
2284
2285
        $expectedIdList = [];
2286
        foreach ($expected as $contentNumber) {
2287
            $expectedIdList[] = $contentIdList[$contentNumber];
2288
        }
2289
2290
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2291
    }
2292
2293
    public function providerForTestMultilingualFieldFilter()
2294
    {
2295
        return [
2296
            0 => [
2297
                $fixture = [
2298
                    1 => [null, 1, null, 'ger-DE', true],
2299
                    2 => [4, null, null, 'eng-GB', true],
2300
                    3 => [3, null, null, 'eng-GB', false],
2301
                    4 => [null, 2, null, 'ger-DE', false],
2302
                    5 => [5, null, null, 'eng-GB', true],
2303
                ],
2304
                $languageSettings = [
2305
                    'languages' => [
2306
                        'ger-DE',
2307
                    ],
2308
                ],
2309
                new Criterion\Field('integer', Criterion\Operator::LT, 5),
2310
                /**
2311
                 * Expected order, Value eng-GB, Value ger-DE.
2312
                 *
2313
                 * Content 2, 4, -
2314
                 * Content 4, -, 2
2315
                 * Content 1, -, 1
2316
                 */
2317
                [2, 4, 1],
2318
            ],
2319
            1 => [
2320
                $fixture,
2321
                [
2322
                    'languages' => [
2323
                        'ger-DE',
2324
                    ],
2325
                    'useAlwaysAvailable' => false,
2326
                ],
2327
                new Criterion\Field('integer', Criterion\Operator::LT, 2),
2328
                /**
2329
                 * Expected order, Value eng-GB, Value ger-DE.
2330
                 *
2331
                 * Content 1, -, 1
2332
                 */
2333
                [1],
2334
            ],
2335
            2 => [
2336
                $fixture,
2337
                [
2338
                    'languages' => [
2339
                        'eng-GB',
2340
                    ],
2341
                ],
2342
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2343
                /**
2344
                 * Expected order, Value eng-GB, Value ger-DE.
2345
                 *
2346
                 * Content 5, 5, -
2347
                 * Content 2, 4, -
2348
                 * Content 3, 3, -
2349
                 * Content 1, -, 1
2350
                 */
2351
                [2, 3, 1],
2352
            ],
2353
            3 => [
2354
                $fixture,
2355
                [
2356
                    'languages' => [
2357
                        'eng-GB',
2358
                    ],
2359
                    'useAlwaysAvailable' => false,
2360
                ],
2361
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2362
                /**
2363
                 * Expected order, Value eng-GB, Value ger-DE.
2364
                 *
2365
                 * Content 2, 4, -
2366
                 * Content 3, 3, -
2367
                 */
2368
                [2, 3],
2369
            ],
2370
            4 => [
2371
                $fixture,
2372
                $languageSettings,
2373
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2374
                /**
2375
                 * Expected order, Value eng-GB, Value ger-DE.
2376
                 *
2377
                 * Content 2, 4, -
2378
                 * Content 4, -, 2
2379
                 * Content 1, -, 1
2380
                 */
2381
                [2, 4, 1],
2382
            ],
2383
            5 => [
2384
                $fixture,
2385
                $languageSettings,
2386
                new Criterion\Field('integer', Criterion\Operator::GT, 1),
2387
                /**
2388
                 * Expected order, Value eng-GB, Value ger-DE.
2389
                 *
2390
                 * Content 5, 5, -
2391
                 * Content 2, 4, -
2392
                 * Content 4, -, 2
2393
                 */
2394
                [5, 2, 4],
2395
            ],
2396
            6 => [
2397
                $fixture,
2398
                $languageSettings,
2399
                new Criterion\Field('integer', Criterion\Operator::GTE, 2),
2400
                /**
2401
                 * Expected order, Value eng-GB, Value ger-DE.
2402
                 *
2403
                 * Content 5, 5, -
2404
                 * Content 2, 4, -
2405
                 * Content 4, -, 2
2406
                 */
2407
                [5, 2, 4],
2408
            ],
2409
            7 => [
2410
                $fixture,
2411
                $languageSettings,
2412
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]),
2413
                /**
2414
                 * Expected order, Value eng-GB, Value ger-DE.
2415
                 *
2416
                 * Content 2, 4, -
2417
                 * Content 4, -, 2
2418
                 */
2419
                [2, 4],
2420
            ],
2421
            8 => [
2422
                $fixture,
2423
                $languageSettings,
2424
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]),
2425
                [],
2426
            ],
2427
            9 => [
2428
                $fixture,
2429
                $languageSettings,
2430
                new Criterion\Field('integer', Criterion\Operator::EQ, 4),
2431
                /**
2432
                 * Expected order, Value eng-GB, Value ger-DE.
2433
                 *
2434
                 * Content 4, -, 2
2435
                 */
2436
                [2],
2437
            ],
2438
            10 => [
2439
                $fixture,
2440
                $languageSettings,
2441
                new Criterion\Field('integer', Criterion\Operator::EQ, 2),
2442
                /**
2443
                 * Expected order, Value eng-GB, Value ger-DE.
2444
                 *
2445
                 * Content 2, 4, -
2446
                 */
2447
                [4],
2448
            ],
2449
        ];
2450
    }
2451
2452
    /**
2453
     * Test for the findContent() method.
2454
     *
2455
     * @group ttt
2456
     * @dataProvider providerForTestMultilingualFieldFilter
2457
     *
2458
     * @param array $contentDataList
2459
     * @param array $languageSettings
2460
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2461
     * @param array $expected
2462
     */
2463
    public function testMultilingualFieldFilterContent(
2464
        array $contentDataList,
2465
        $languageSettings,
2466
        Criterion $criterion,
2467
        $expected
2468
    ) {
2469
        $this->assertMultilingualFieldFilter(
2470
            $contentDataList,
2471
            $languageSettings,
2472
            $criterion,
2473
            $expected
2474
        );
2475
    }
2476
2477
    /**
2478
     * Test for the findLocations() method.
2479
     *
2480
     * @group ttt
2481
     * @dataProvider providerForTestMultilingualFieldFilter
2482
     *
2483
     * @param array $contentDataList
2484
     * @param array $languageSettings
2485
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2486
     * @param array $expected
2487
     */
2488
    public function testMultilingualFieldFilterLocation(
2489
        array $contentDataList,
2490
        $languageSettings,
2491
        Criterion $criterion,
2492
        $expected
2493
    ) {
2494
        $this->assertMultilingualFieldFilter(
2495
            $contentDataList,
2496
            $languageSettings,
2497
            $criterion,
2498
            $expected,
2499
            false
2500
        );
2501
    }
2502
2503
    /**
2504
     * @param array $contentDataList
2505
     * @param array $languageSettings
2506
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2507
     * @param array $expected
2508
     * @param bool $contentSearch
2509
     */
2510
    protected function assertMultilingualFieldFilter(
2511
        array $contentDataList,
2512
        $languageSettings,
2513
        Criterion $criterion,
2514
        $expected,
2515
        $contentSearch = true
2516
    ) {
2517
        $this->checkPrioritizedLanguagesSupport();
2518
        $contentType = $this->createTestContentType();
2519
2520
        // Create a draft to account for behaviour with ContentType in different states
2521
        $repository = $this->getRepository();
2522
        $contentTypeService = $repository->getContentTypeService();
2523
        $contentTypeService->createContentTypeDraft($contentType);
2524
2525
        $defaults = [null, null, null, 'eng-GB', false];
2526
        $contentIdList = [];
2527 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...
2528
            $contentData = $contentData + $defaults;
2529
            list(
2530
                $fieldValue11,
2531
                $fieldValue12,
2532
                $fieldValue2,
2533
                $mainLanguageCode,
2534
                $alwaysAvailable
2535
            ) = $contentData;
2536
2537
            $contentIdList[$key] = $this->createMultilingualContent(
2538
                $contentType,
2539
                $fieldValue11,
2540
                $fieldValue12,
2541
                $fieldValue2,
2542
                $mainLanguageCode,
2543
                $alwaysAvailable
2544
            )->id;
2545
        }
2546
2547
        $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC);
2548
        $searchService = $repository->getSearchService();
2549
        if ($contentSearch) {
2550
            $query = new Query(
2551
                [
2552
                    'query' => new Criterion\LogicalAnd(
2553
                        [
2554
                            new Criterion\ContentTypeId($contentType->id),
2555
                            $criterion,
2556
                        ]
2557
                    ),
2558
                    'sortClauses' => [$sortClause],
2559
                ]
2560
            );
2561
            $result = $searchService->findContent($query, $languageSettings);
2562
        } else {
2563
            $query = new LocationQuery(
2564
                [
2565
                    'query' => new Criterion\LogicalAnd(
2566
                        [
2567
                            new Criterion\ContentTypeId($contentType->id),
2568
                            $criterion,
2569
                        ]
2570
                    ),
2571
                    'sortClauses' => [$sortClause],
2572
                ]
2573
            );
2574
            $result = $searchService->findLocations($query, $languageSettings);
2575
        }
2576
2577
        $this->assertEquals(count($expected), $result->totalCount);
2578
2579
        $expectedIdList = [];
2580
        foreach ($expected as $contentNumber) {
2581
            $expectedIdList[] = $contentIdList[$contentNumber];
2582
        }
2583
2584
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2585
    }
2586
2587
    /**
2588
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result
2589
     *
2590
     * @return array
2591
     */
2592
    protected function mapResultContentIds(SearchResult $result)
2593
    {
2594
        return array_map(
2595
            function (SearchHit $searchHit) {
2596
                if ($searchHit->valueObject instanceof Location) {
2597
                    return $searchHit->valueObject->contentInfo->id;
2598
                }
2599
2600
                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...
2601
            },
2602
            $result->searchHits
2603
        );
2604
    }
2605
2606
    /**
2607
     * Test for the findContent() method.
2608
     *
2609
     * @dataProvider getSortedContentSearches
2610
     *
2611
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2612
     */
2613
    public function testFindAndSortContent($queryData, $fixture, $closure = null)
2614
    {
2615
        $query = new Query($queryData);
2616
        $this->assertQueryFixture($query, $fixture, $closure);
2617
    }
2618
2619
    /**
2620
     * Test for the findContentInfo() method.
2621
     *
2622
     * @dataProvider getSortedContentSearches
2623
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2624
     */
2625
    public function testFindAndSortContentInfo($queryData, $fixture, $closure = null)
2626
    {
2627
        $query = new Query($queryData);
2628
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
2629
    }
2630
2631
    /**
2632
     * Test for the findLocations() method.
2633
     *
2634
     * @dataProvider getSortedContentSearches
2635
     *
2636
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2637
     */
2638
    public function testFindAndSortContentLocations($queryData, $fixture, $closure = null)
2639
    {
2640
        $query = new LocationQuery($queryData);
2641
        $this->assertQueryFixture($query, $fixture, $closure);
2642
    }
2643
2644
    /**
2645
     * Test for the findLocations() method.
2646
     *
2647
     * @dataProvider getSortedLocationSearches
2648
     *
2649
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2650
     */
2651
    public function testFindAndSortLocations($queryData, $fixture, $closure = null)
2652
    {
2653
        $query = new LocationQuery($queryData);
2654
        $this->assertQueryFixture($query, $fixture, $closure);
2655
    }
2656
2657
    /**
2658
     * Test for the findContent() method.
2659
     *
2660
     * @dataProvider getFacetedSearches
2661
     *
2662
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2663
     */
2664
    public function testFindFacetedContent(Query $query, $fixture)
2665
    {
2666
        $this->assertQueryFixture($query, $fixture);
2667
    }
2668
2669
    /**
2670
     * Test for the findContentInfo() method.
2671
     *
2672
     * @dataProvider getFacetedSearches
2673
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2674
     */
2675
    public function testFindFacetedContentInfo(Query $query, $fixture)
2676
    {
2677
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true);
2678
    }
2679
2680
    /**
2681
     * Test for the findContent() method.
2682
     *
2683
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2684
     */
2685 View Code Duplication
    public function testQueryCustomField()
2686
    {
2687
        $query = new Query(
2688
            [
2689
                'query' => new Criterion\CustomField(
2690
                    'custom_field',
2691
                    Criterion\Operator::EQ,
2692
                    'AdMiNiStRaToR'
2693
                ),
2694
                'offset' => 0,
2695
                'limit' => 10,
2696
                'sortClauses' => [new SortClause\ContentId()],
2697
            ]
2698
        );
2699
        $this->assertQueryFixture(
2700
            $query,
2701
            $this->getFixtureDir() . '/QueryCustomField.php'
2702
        );
2703
    }
2704
2705
    /**
2706
     * Test for the findContent() method.
2707
     *
2708
     * This tests explicitely queries the first_name while user is contained in
2709
     * the last_name of admin and anonymous. This is done to show the custom
2710
     * copy field working.
2711
     *
2712
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2713
     */
2714 View Code Duplication
    public function testQueryModifiedField()
2715
    {
2716
        // Check using get_class since the others extend SetupFactory\Legacy
2717
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2718
            $this->markTestIncomplete(
2719
                'Custom fields not supported by LegacySE ' .
2720
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2721
            );
2722
        }
2723
2724
        $query = new Query(
2725
            [
2726
                'query' => new Criterion\Field(
2727
                    'first_name',
2728
                    Criterion\Operator::EQ,
2729
                    'User'
2730
                ),
2731
                'offset' => 0,
2732
                'limit' => 10,
2733
                'sortClauses' => [new SortClause\ContentId()],
2734
            ]
2735
        );
2736
        $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...
2737
2738
        $this->assertQueryFixture(
2739
            $query,
2740
            $this->getFixtureDir() . '/QueryModifiedField.php'
2741
        );
2742
    }
2743
2744
    /**
2745
     * Test for the findContent() method.
2746
     *
2747
     * This tests first explicitly creates sort clause on the 'short_name' which is empty
2748
     * for all Content instances of 'folder' ContentType. Custom sort field is then set
2749
     * to the index storage name of folder's 'name' field, in order to show the custom
2750
     * sort field working.
2751
     *
2752
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2753
     */
2754
    public function testSortModifiedField()
2755
    {
2756
        // Check using get_class since the others extend SetupFactory\Legacy
2757
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2758
            $this->markTestIncomplete(
2759
                'Custom field sort not supported by LegacySE ' .
2760
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2761
            );
2762
        }
2763
2764
        $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC);
2765
        $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s');
2766
2767
        $query = new Query(
2768
            [
2769
                'filter' => new Criterion\ContentTypeId(1),
2770
                'offset' => 0,
2771
                'limit' => 10,
2772
                'sortClauses' => [
2773
                    $sortClause,
2774
                    new SortClause\ContentId(),
2775
                ],
2776
            ]
2777
        );
2778
2779
        $this->assertQueryFixture(
2780
            $query,
2781
            $this->getFixtureDir() . '/SortFolderName.php'
2782
        );
2783
    }
2784
2785
    /**
2786
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
2787
     */
2788 View Code Duplication
    protected function createTestPlaceContentType()
2789
    {
2790
        $repository = $this->getRepository();
2791
        $contentTypeService = $repository->getContentTypeService();
2792
2793
        $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype');
2794
        $createStruct->mainLanguageCode = 'eng-GB';
2795
        $createStruct->names = ['eng-GB' => 'Test type'];
2796
        $createStruct->creatorId = 14;
2797
        $createStruct->creationDate = new \DateTime();
2798
2799
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation');
2800
        $translatableFieldCreate->names = ['eng-GB' => 'Map location field'];
2801
        $translatableFieldCreate->fieldGroup = 'main';
2802
        $translatableFieldCreate->position = 1;
2803
        $translatableFieldCreate->isTranslatable = false;
2804
        $translatableFieldCreate->isSearchable = true;
2805
2806
        $createStruct->addFieldDefinition($translatableFieldCreate);
2807
2808
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
2809
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
2810
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
2811
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
2812
2813
        return $contentType;
2814
    }
2815
2816
    /**
2817
     * Test for the findContent() method.
2818
     *
2819
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2820
     * @group maplocation
2821
     */
2822 View Code Duplication
    public function testMapLocationDistanceLessThanOrEqual()
2823
    {
2824
        $contentType = $this->createTestPlaceContentType();
2825
2826
        // Create a draft to account for behaviour with ContentType in different states
2827
        $repository = $this->getRepository();
2828
        $contentTypeService = $repository->getContentTypeService();
2829
        $contentService = $repository->getContentService();
2830
        $contentTypeService->createContentTypeDraft($contentType);
2831
2832
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2833
        $createStruct->alwaysAvailable = false;
2834
        $createStruct->mainLanguageCode = 'eng-GB';
2835
        $createStruct->setField(
2836
            'maplocation',
2837
            [
2838
                'latitude' => 45.894877,
2839
                'longitude' => 15.972699,
2840
                'address' => 'Here be wild boars',
2841
            ],
2842
            'eng-GB'
2843
        );
2844
2845
        $draft = $contentService->createContent($createStruct);
2846
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2847
2848
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2849
        $createStruct->alwaysAvailable = false;
2850
        $createStruct->mainLanguageCode = 'eng-GB';
2851
        $createStruct->setField(
2852
            'maplocation',
2853
            [
2854
                'latitude' => 45.927334,
2855
                'longitude' => 15.934847,
2856
                'address' => 'A lone tree',
2857
            ],
2858
            'eng-GB'
2859
        );
2860
2861
        $draft = $contentService->createContent($createStruct);
2862
        $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...
2863
2864
        $this->refreshSearch($repository);
2865
2866
        $query = new Query(
2867
            [
2868
                'filter' => new Criterion\LogicalAnd(
2869
                    [
2870
                        new Criterion\ContentTypeId($contentType->id),
2871
                        new Criterion\MapLocationDistance(
2872
                            'maplocation',
2873
                            Criterion\Operator::LTE,
2874
                            240,
2875
                            43.756825,
2876
                            15.775074
2877
                        ),
2878
                    ]
2879
                ),
2880
                'offset' => 0,
2881
                'limit' => 10,
2882
                'sortClauses' => [],
2883
            ]
2884
        );
2885
2886
        $searchService = $repository->getSearchService();
2887
        $result = $searchService->findContent($query);
2888
2889
        $this->assertEquals(1, $result->totalCount);
2890
        $this->assertEquals(
2891
            $wildBoars->id,
2892
            $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...
2893
        );
2894
    }
2895
2896
    /**
2897
     * Test for the findContent() method.
2898
     *
2899
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2900
     * @group maplocation
2901
     */
2902 View Code Duplication
    public function testMapLocationDistanceGreaterThanOrEqual()
2903
    {
2904
        $contentType = $this->createTestPlaceContentType();
2905
2906
        // Create a draft to account for behaviour with ContentType in different states
2907
        $repository = $this->getRepository();
2908
        $contentTypeService = $repository->getContentTypeService();
2909
        $contentService = $repository->getContentService();
2910
        $contentTypeService->createContentTypeDraft($contentType);
2911
2912
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2913
        $createStruct->alwaysAvailable = false;
2914
        $createStruct->mainLanguageCode = 'eng-GB';
2915
        $createStruct->setField(
2916
            'maplocation',
2917
            [
2918
                'latitude' => 45.894877,
2919
                'longitude' => 15.972699,
2920
                'address' => 'Here be wild boars',
2921
            ],
2922
            'eng-GB'
2923
        );
2924
2925
        $draft = $contentService->createContent($createStruct);
2926
        $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...
2927
2928
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2929
        $createStruct->alwaysAvailable = false;
2930
        $createStruct->mainLanguageCode = 'eng-GB';
2931
        $createStruct->setField(
2932
            'maplocation',
2933
            [
2934
                'latitude' => 45.927334,
2935
                'longitude' => 15.934847,
2936
                'address' => 'A lone tree',
2937
            ],
2938
            'eng-GB'
2939
        );
2940
2941
        $draft = $contentService->createContent($createStruct);
2942
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2943
2944
        $this->refreshSearch($repository);
2945
2946
        $query = new Query(
2947
            [
2948
                'filter' => new Criterion\LogicalAnd(
2949
                    [
2950
                        new Criterion\ContentTypeId($contentType->id),
2951
                        new Criterion\MapLocationDistance(
2952
                            'maplocation',
2953
                            Criterion\Operator::GTE,
2954
                            240,
2955
                            43.756825,
2956
                            15.775074
2957
                        ),
2958
                    ]
2959
                ),
2960
                'offset' => 0,
2961
                'limit' => 10,
2962
                'sortClauses' => [],
2963
            ]
2964
        );
2965
2966
        $searchService = $repository->getSearchService();
2967
        $result = $searchService->findContent($query);
2968
2969
        $this->assertEquals(1, $result->totalCount);
2970
        $this->assertEquals(
2971
            $tree->id,
2972
            $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...
2973
        );
2974
    }
2975
2976
    /**
2977
     * Test for the findContent() method.
2978
     *
2979
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2980
     * @group maplocation
2981
     */
2982
    public function testMapLocationDistanceBetween()
2983
    {
2984
        $contentType = $this->createTestPlaceContentType();
2985
2986
        // Create a draft to account for behaviour with ContentType in different states
2987
        $repository = $this->getRepository();
2988
        $contentTypeService = $repository->getContentTypeService();
2989
        $contentService = $repository->getContentService();
2990
        $contentTypeService->createContentTypeDraft($contentType);
2991
2992
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2993
        $createStruct->alwaysAvailable = false;
2994
        $createStruct->mainLanguageCode = 'eng-GB';
2995
        $createStruct->setField(
2996
            'maplocation',
2997
            [
2998
                'latitude' => 45.894877,
2999
                'longitude' => 15.972699,
3000
                'address' => 'Here be wild boars',
3001
            ],
3002
            'eng-GB'
3003
        );
3004
3005
        $draft = $contentService->createContent($createStruct);
3006
        $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...
3007
3008
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3009
        $createStruct->alwaysAvailable = false;
3010
        $createStruct->mainLanguageCode = 'eng-GB';
3011
        $createStruct->setField(
3012
            'maplocation',
3013
            [
3014
                'latitude' => 45.927334,
3015
                'longitude' => 15.934847,
3016
                'address' => 'A lone tree',
3017
            ],
3018
            'eng-GB'
3019
        );
3020
3021
        $draft = $contentService->createContent($createStruct);
3022
        $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...
3023
3024
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3025
        $createStruct->alwaysAvailable = false;
3026
        $createStruct->mainLanguageCode = 'eng-GB';
3027
        $createStruct->setField(
3028
            'maplocation',
3029
            [
3030
                'latitude' => 45.903777,
3031
                'longitude' => 15.958788,
3032
                'address' => 'Meadow with mushrooms',
3033
            ],
3034
            'eng-GB'
3035
        );
3036
3037
        $draft = $contentService->createContent($createStruct);
3038
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3039
3040
        $this->refreshSearch($repository);
3041
3042
        $query = new Query(
3043
            [
3044
                'filter' => new Criterion\LogicalAnd(
3045
                    [
3046
                        new Criterion\ContentTypeId($contentType->id),
3047
                        new Criterion\MapLocationDistance(
3048
                            'maplocation',
3049
                            Criterion\Operator::BETWEEN,
3050
                            [239, 241],
3051
                            43.756825,
3052
                            15.775074
3053
                        ),
3054
                    ]
3055
                ),
3056
                'offset' => 0,
3057
                'limit' => 10,
3058
                'sortClauses' => [],
3059
            ]
3060
        );
3061
3062
        $searchService = $repository->getSearchService();
3063
        $result = $searchService->findContent($query);
3064
3065
        $this->assertEquals(1, $result->totalCount);
3066
        $this->assertEquals(
3067
            $mushrooms->id,
3068
            $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...
3069
        );
3070
    }
3071
3072
    /**
3073
     * Test for the findContent() method.
3074
     *
3075
     * This tests the distance over the pole. The tests intentionally uses large range,
3076
     * as the flat Earth model used in Legacy Storage Search is not precise for the use case.
3077
     * What is tested here is that outer bounding box is correctly calculated, so that
3078
     * location is not excluded.
3079
     *
3080
     * Range between 222km and 350km shows the magnitude of error between great-circle
3081
     * (always very precise) and flat Earth (very imprecise for this use case) models.
3082
     *
3083
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3084
     * @group maplocation
3085
     */
3086
    public function testMapLocationDistanceBetweenPolar()
3087
    {
3088
        $contentType = $this->createTestPlaceContentType();
3089
3090
        // Create a draft to account for behaviour with ContentType in different states
3091
        $repository = $this->getRepository();
3092
        $contentTypeService = $repository->getContentTypeService();
3093
        $contentService = $repository->getContentService();
3094
        $contentTypeService->createContentTypeDraft($contentType);
3095
3096
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3097
        $createStruct->alwaysAvailable = false;
3098
        $createStruct->mainLanguageCode = 'eng-GB';
3099
        $createStruct->setField(
3100
            'maplocation',
3101
            [
3102
                'latitude' => 89,
3103
                'longitude' => -164,
3104
                'address' => 'Polar bear media tower',
3105
            ],
3106
            'eng-GB'
3107
        );
3108
3109
        $draft = $contentService->createContent($createStruct);
3110
        $polarBear = $contentService->publishVersion($draft->getVersionInfo());
3111
3112
        $this->refreshSearch($repository);
3113
3114
        $query = new Query(
3115
            [
3116
                'filter' => new Criterion\LogicalAnd(
3117
                    [
3118
                        new Criterion\ContentTypeId($contentType->id),
3119
                        new Criterion\MapLocationDistance(
3120
                            'maplocation',
3121
                            Criterion\Operator::BETWEEN,
3122
                            [221, 350],
3123
                            89,
3124
                            16
3125
                        ),
3126
                    ]
3127
                ),
3128
                'offset' => 0,
3129
                'limit' => 10,
3130
                'sortClauses' => [],
3131
            ]
3132
        );
3133
3134
        $searchService = $repository->getSearchService();
3135
        $result = $searchService->findContent($query);
3136
3137
        $this->assertEquals(1, $result->totalCount);
3138
        $this->assertEquals(
3139
            $polarBear->id,
3140
            $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...
3141
        );
3142
    }
3143
3144
    /**
3145
     * Test for the findContent() method.
3146
     *
3147
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3148
     * @group maplocation
3149
     */
3150 View Code Duplication
    public function testMapLocationDistanceSortAscending()
3151
    {
3152
        $contentType = $this->createTestPlaceContentType();
3153
3154
        // Create a draft to account for behaviour with ContentType in different states
3155
        $repository = $this->getRepository();
3156
        $contentTypeService = $repository->getContentTypeService();
3157
        $contentService = $repository->getContentService();
3158
        $contentTypeService->createContentTypeDraft($contentType);
3159
3160
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3161
        $createStruct->alwaysAvailable = false;
3162
        $createStruct->mainLanguageCode = 'eng-GB';
3163
        $createStruct->setField(
3164
            'maplocation',
3165
            [
3166
                'latitude' => 45.894877,
3167
                'longitude' => 15.972699,
3168
                'address' => 'Here be wild boars',
3169
            ],
3170
            'eng-GB'
3171
        );
3172
3173
        $draft = $contentService->createContent($createStruct);
3174
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3175
3176
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3177
        $createStruct->alwaysAvailable = false;
3178
        $createStruct->mainLanguageCode = 'eng-GB';
3179
        $createStruct->setField(
3180
            'maplocation',
3181
            [
3182
                'latitude' => 45.927334,
3183
                'longitude' => 15.934847,
3184
                'address' => 'A lone tree',
3185
            ],
3186
            'eng-GB'
3187
        );
3188
3189
        $draft = $contentService->createContent($createStruct);
3190
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3191
3192
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3193
        $createStruct->alwaysAvailable = false;
3194
        $createStruct->mainLanguageCode = 'eng-GB';
3195
        $createStruct->setField(
3196
            'maplocation',
3197
            [
3198
                'latitude' => 45.903777,
3199
                'longitude' => 15.958788,
3200
                'address' => 'Meadow with mushrooms',
3201
            ],
3202
            'eng-GB'
3203
        );
3204
3205
        $draft = $contentService->createContent($createStruct);
3206
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3207
3208
        $this->refreshSearch($repository);
3209
3210
        $wellInVodice = [
3211
            'latitude' => 43.756825,
3212
            'longitude' => 15.775074,
3213
        ];
3214
3215
        $query = new Query(
3216
            [
3217
                'filter' => new Criterion\LogicalAnd(
3218
                    [
3219
                        new Criterion\ContentTypeId($contentType->id),
3220
                        new Criterion\MapLocationDistance(
3221
                            'maplocation',
3222
                            Criterion\Operator::GTE,
3223
                            235,
3224
                            $wellInVodice['latitude'],
3225
                            $wellInVodice['longitude']
3226
                        ),
3227
                    ]
3228
                ),
3229
                'offset' => 0,
3230
                'limit' => 10,
3231
                'sortClauses' => [
3232
                    new SortClause\MapLocationDistance(
3233
                        'testtype',
3234
                        'maplocation',
3235
                        $wellInVodice['latitude'],
3236
                        $wellInVodice['longitude'],
3237
                        Query::SORT_ASC
3238
                    ),
3239
                ],
3240
            ]
3241
        );
3242
3243
        $searchService = $repository->getSearchService();
3244
        $result = $searchService->findContent($query);
3245
3246
        $this->assertEquals(3, $result->totalCount);
3247
        $this->assertEquals(
3248
            $wildBoars->id,
3249
            $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...
3250
        );
3251
        $this->assertEquals(
3252
            $mushrooms->id,
3253
            $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...
3254
        );
3255
        $this->assertEquals(
3256
            $tree->id,
3257
            $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...
3258
        );
3259
    }
3260
3261
    /**
3262
     * Test for the findContent() method.
3263
     *
3264
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3265
     * @group maplocation
3266
     */
3267 View Code Duplication
    public function testMapLocationDistanceSortDescending()
3268
    {
3269
        $contentType = $this->createTestPlaceContentType();
3270
3271
        // Create a draft to account for behaviour with ContentType in different states
3272
        $repository = $this->getRepository();
3273
        $contentTypeService = $repository->getContentTypeService();
3274
        $contentService = $repository->getContentService();
3275
        $contentTypeService->createContentTypeDraft($contentType);
3276
3277
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3278
        $createStruct->alwaysAvailable = false;
3279
        $createStruct->mainLanguageCode = 'eng-GB';
3280
        $createStruct->setField(
3281
            'maplocation',
3282
            [
3283
                'latitude' => 45.894877,
3284
                'longitude' => 15.972699,
3285
                'address' => 'Here be wild boars',
3286
            ],
3287
            'eng-GB'
3288
        );
3289
3290
        $draft = $contentService->createContent($createStruct);
3291
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3292
3293
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3294
        $createStruct->alwaysAvailable = false;
3295
        $createStruct->mainLanguageCode = 'eng-GB';
3296
        $createStruct->setField(
3297
            'maplocation',
3298
            [
3299
                'latitude' => 45.927334,
3300
                'longitude' => 15.934847,
3301
                'address' => 'A lone tree',
3302
            ],
3303
            'eng-GB'
3304
        );
3305
3306
        $draft = $contentService->createContent($createStruct);
3307
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3308
3309
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3310
        $createStruct->alwaysAvailable = false;
3311
        $createStruct->mainLanguageCode = 'eng-GB';
3312
        $createStruct->setField(
3313
            'maplocation',
3314
            [
3315
                'latitude' => 45.903777,
3316
                'longitude' => 15.958788,
3317
                'address' => 'Meadow with mushrooms',
3318
            ],
3319
            'eng-GB'
3320
        );
3321
3322
        $draft = $contentService->createContent($createStruct);
3323
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3324
3325
        $this->refreshSearch($repository);
3326
3327
        $well = [
3328
            'latitude' => 43.756825,
3329
            'longitude' => 15.775074,
3330
        ];
3331
3332
        $query = new Query(
3333
            [
3334
                'filter' => new Criterion\LogicalAnd(
3335
                    [
3336
                        new Criterion\ContentTypeId($contentType->id),
3337
                        new Criterion\MapLocationDistance(
3338
                            'maplocation',
3339
                            Criterion\Operator::GTE,
3340
                            235,
3341
                            $well['latitude'],
3342
                            $well['longitude']
3343
                        ),
3344
                    ]
3345
                ),
3346
                'offset' => 0,
3347
                'limit' => 10,
3348
                'sortClauses' => [
3349
                    new SortClause\MapLocationDistance(
3350
                        'testtype',
3351
                        'maplocation',
3352
                        $well['latitude'],
3353
                        $well['longitude'],
3354
                        Query::SORT_DESC
3355
                    ),
3356
                ],
3357
            ]
3358
        );
3359
3360
        $searchService = $repository->getSearchService();
3361
        $result = $searchService->findContent($query);
3362
3363
        $this->assertEquals(3, $result->totalCount);
3364
        $this->assertEquals(
3365
            $wildBoars->id,
3366
            $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...
3367
        );
3368
        $this->assertEquals(
3369
            $mushrooms->id,
3370
            $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...
3371
        );
3372
        $this->assertEquals(
3373
            $tree->id,
3374
            $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...
3375
        );
3376
    }
3377
3378
    /**
3379
     * Test for the findContent() method.
3380
     *
3381
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3382
     * @group maplocation
3383
     */
3384
    public function testMapLocationDistanceWithCustomField()
3385
    {
3386
        $setupFactory = $this->getSetupFactory();
3387
        if ($setupFactory instanceof LegacyElasticsearch) {
3388
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3389
        }
3390
3391
        $contentType = $this->createTestPlaceContentType();
3392
3393
        // Create a draft to account for behaviour with ContentType in different states
3394
        $repository = $this->getRepository();
3395
        $contentTypeService = $repository->getContentTypeService();
3396
        $contentService = $repository->getContentService();
3397
        $contentTypeService->createContentTypeDraft($contentType);
3398
3399
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3400
        $createStruct->alwaysAvailable = false;
3401
        $createStruct->mainLanguageCode = 'eng-GB';
3402
        $createStruct->setField(
3403
            'maplocation',
3404
            [
3405
                'latitude' => 45.894877,
3406
                'longitude' => 15.972699,
3407
                'address' => 'Here be wild boars',
3408
            ],
3409
            'eng-GB'
3410
        );
3411
3412
        $draft = $contentService->createContent($createStruct);
3413
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3414
3415
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3416
        $createStruct->alwaysAvailable = false;
3417
        $createStruct->mainLanguageCode = 'eng-GB';
3418
        $createStruct->setField(
3419
            'maplocation',
3420
            [
3421
                'latitude' => 45.927334,
3422
                'longitude' => 15.934847,
3423
                'address' => 'A lone tree',
3424
            ],
3425
            'eng-GB'
3426
        );
3427
3428
        $draft = $contentService->createContent($createStruct);
3429
        $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...
3430
3431
        $this->refreshSearch($repository);
3432
3433
        $distanceCriterion = new Criterion\MapLocationDistance(
3434
            'maplocation',
3435
            Criterion\Operator::LTE,
3436
            240,
3437
            43.756825,
3438
            15.775074
3439
        );
3440
        $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3441
3442
        $query = new Query(
3443
            [
3444
                'filter' => new Criterion\LogicalAnd(
3445
                    [
3446
                        new Criterion\ContentTypeId($contentType->id),
3447
                        $distanceCriterion,
3448
                    ]
3449
                ),
3450
                'offset' => 0,
3451
                'limit' => 10,
3452
                'sortClauses' => [],
3453
            ]
3454
        );
3455
3456
        $searchService = $repository->getSearchService();
3457
        $result = $searchService->findContent($query);
3458
3459
        $this->assertEquals(1, $result->totalCount);
3460
        $this->assertEquals(
3461
            $wildBoars->id,
3462
            $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...
3463
        );
3464
    }
3465
3466
    /**
3467
     * Test for the findContent() method.
3468
     *
3469
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3470
     * @group maplocation
3471
     */
3472
    public function testMapLocationDistanceWithCustomFieldSort()
3473
    {
3474
        $setupFactory = $this->getSetupFactory();
3475
        if ($setupFactory instanceof LegacyElasticsearch) {
3476
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3477
        }
3478
3479
        $contentType = $this->createTestPlaceContentType();
3480
3481
        // Create a draft to account for behaviour with ContentType in different states
3482
        $repository = $this->getRepository();
3483
        $contentTypeService = $repository->getContentTypeService();
3484
        $contentService = $repository->getContentService();
3485
        $contentTypeService->createContentTypeDraft($contentType);
3486
3487
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3488
        $createStruct->alwaysAvailable = false;
3489
        $createStruct->mainLanguageCode = 'eng-GB';
3490
        $createStruct->setField(
3491
            'maplocation',
3492
            [
3493
                'latitude' => 45.894877,
3494
                'longitude' => 15.972699,
3495
                'address' => 'Here be wild boars',
3496
            ],
3497
            'eng-GB'
3498
        );
3499
3500
        $draft = $contentService->createContent($createStruct);
3501
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3502
3503
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3504
        $createStruct->alwaysAvailable = false;
3505
        $createStruct->mainLanguageCode = 'eng-GB';
3506
        $createStruct->setField(
3507
            'maplocation',
3508
            [
3509
                'latitude' => 45.927334,
3510
                'longitude' => 15.934847,
3511
                'address' => 'A lone tree',
3512
            ],
3513
            'eng-GB'
3514
        );
3515
3516
        $draft = $contentService->createContent($createStruct);
3517
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3518
3519
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3520
        $createStruct->alwaysAvailable = false;
3521
        $createStruct->mainLanguageCode = 'eng-GB';
3522
        $createStruct->setField(
3523
            'maplocation',
3524
            [
3525
                'latitude' => 45.903777,
3526
                'longitude' => 15.958788,
3527
                'address' => 'Meadow with mushrooms',
3528
            ],
3529
            'eng-GB'
3530
        );
3531
3532
        $draft = $contentService->createContent($createStruct);
3533
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3534
3535
        $this->refreshSearch($repository);
3536
3537
        $well = [
3538
            'latitude' => 43.756825,
3539
            'longitude' => 15.775074,
3540
        ];
3541
3542
        $sortClause = new SortClause\MapLocationDistance(
3543
            'testtype',
3544
            'maplocation',
3545
            $well['latitude'],
3546
            $well['longitude'],
3547
            Query::SORT_DESC
3548
        );
3549
        $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3550
3551
        $query = new Query(
3552
            [
3553
                'filter' => new Criterion\LogicalAnd(
3554
                    [
3555
                        new Criterion\ContentTypeId($contentType->id),
3556
                        new Criterion\MapLocationDistance(
3557
                            'maplocation',
3558
                            Criterion\Operator::GTE,
3559
                            235,
3560
                            $well['latitude'],
3561
                            $well['longitude']
3562
                        ),
3563
                    ]
3564
                ),
3565
                'offset' => 0,
3566
                'limit' => 10,
3567
                'sortClauses' => [
3568
                    $sortClause,
3569
                ],
3570
            ]
3571
        );
3572
3573
        $searchService = $repository->getSearchService();
3574
        $result = $searchService->findContent($query);
3575
3576
        $this->assertEquals(3, $result->totalCount);
3577
        $this->assertEquals(
3578
            $wildBoars->id,
3579
            $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...
3580
        );
3581
        $this->assertEquals(
3582
            $mushrooms->id,
3583
            $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...
3584
        );
3585
        $this->assertEquals(
3586
            $tree->id,
3587
            $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...
3588
        );
3589
    }
3590
3591
    /**
3592
     * Test for the findLocations() method.
3593
     *
3594
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3595
     */
3596 View Code Duplication
    public function testFindMainLocation()
3597
    {
3598
        $plainSiteLocationId = 56;
3599
        $designLocationId = 58;
3600
        $partnersContentId = 59;
3601
        $repository = $this->getRepository();
3602
        $locationService = $repository->getLocationService();
3603
        $contentService = $repository->getContentService();
3604
3605
        // Add secondary Location for "Partners" user group, under "Design" page
3606
        $locationService->createLocation(
3607
            $contentService->loadContentInfo($partnersContentId),
3608
            $locationService->newLocationCreateStruct($designLocationId)
3609
        );
3610
3611
        $this->refreshSearch($repository);
3612
3613
        $query = new LocationQuery(
3614
            [
3615
                'filter' => new Criterion\LogicalAnd(
3616
                    [
3617
                        new Criterion\ParentLocationId($designLocationId),
3618
                        new Criterion\Location\IsMainLocation(
3619
                            Criterion\Location\IsMainLocation::MAIN
3620
                        ),
3621
                    ]
3622
                ),
3623
                'offset' => 0,
3624
                'limit' => 10,
3625
                'sortClauses' => [],
3626
            ]
3627
        );
3628
3629
        $searchService = $repository->getSearchService();
3630
        $result = $searchService->findLocations($query);
3631
3632
        $this->assertEquals(1, $result->totalCount);
3633
        $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...
3634
    }
3635
3636
    /**
3637
     * Test for the findLocations() method.
3638
     *
3639
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3640
     */
3641 View Code Duplication
    public function testFindNonMainLocation()
3642
    {
3643
        $designLocationId = 58;
3644
        $partnersContentId = 59;
3645
        $repository = $this->getRepository();
3646
        $locationService = $repository->getLocationService();
3647
        $contentService = $repository->getContentService();
3648
3649
        // Add secondary Location for "Partners" user group, under "Design" page
3650
        $newLocation = $locationService->createLocation(
3651
            $contentService->loadContentInfo($partnersContentId),
3652
            $locationService->newLocationCreateStruct($designLocationId)
3653
        );
3654
3655
        $this->refreshSearch($repository);
3656
3657
        $query = new LocationQuery(
3658
            [
3659
                'filter' => new Criterion\LogicalAnd(
3660
                    [
3661
                        new Criterion\ParentLocationId($designLocationId),
3662
                        new Criterion\Location\IsMainLocation(
3663
                            Criterion\Location\IsMainLocation::NOT_MAIN
3664
                        ),
3665
                    ]
3666
                ),
3667
                'offset' => 0,
3668
                'limit' => 10,
3669
                'sortClauses' => [],
3670
            ]
3671
        );
3672
3673
        $searchService = $repository->getSearchService();
3674
        $result = $searchService->findLocations($query);
3675
3676
        $this->assertEquals(1, $result->totalCount);
3677
        $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...
3678
    }
3679
3680
    /**
3681
     * Test for the findLocations() method.
3682
     *
3683
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3684
     */
3685 View Code Duplication
    public function testSortMainLocationAscending()
3686
    {
3687
        $plainSiteLocationId = 56;
3688
        $designLocationId = 58;
3689
        $partnersContentId = 59;
3690
        $repository = $this->getRepository();
3691
        $locationService = $repository->getLocationService();
3692
        $contentService = $repository->getContentService();
3693
3694
        // Add secondary Location for "Partners" user group, under "Design" page
3695
        $newLocation = $locationService->createLocation(
3696
            $contentService->loadContentInfo($partnersContentId),
3697
            $locationService->newLocationCreateStruct($designLocationId)
3698
        );
3699
3700
        $this->refreshSearch($repository);
3701
3702
        $query = new LocationQuery(
3703
            [
3704
                'filter' => new Criterion\ParentLocationId($designLocationId),
3705
                'offset' => 0,
3706
                'limit' => 10,
3707
                'sortClauses' => [
3708
                    new SortClause\Location\IsMainLocation(
3709
                        LocationQuery::SORT_ASC
3710
                    ),
3711
                ],
3712
            ]
3713
        );
3714
3715
        $searchService = $repository->getSearchService();
3716
        $result = $searchService->findLocations($query);
3717
3718
        $this->assertEquals(2, $result->totalCount);
3719
        $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...
3720
        $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...
3721
    }
3722
3723
    /**
3724
     * Test for the findLocations() method.
3725
     *
3726
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3727
     */
3728 View Code Duplication
    public function testSortMainLocationDescending()
3729
    {
3730
        $plainSiteLocationId = 56;
3731
        $designLocationId = 58;
3732
        $partnersContentId = 59;
3733
        $repository = $this->getRepository();
3734
        $locationService = $repository->getLocationService();
3735
        $contentService = $repository->getContentService();
3736
3737
        // Add secondary Location for "Partners" user group, under "Design" page
3738
        $newLocation = $locationService->createLocation(
3739
            $contentService->loadContentInfo($partnersContentId),
3740
            $locationService->newLocationCreateStruct($designLocationId)
3741
        );
3742
3743
        $this->refreshSearch($repository);
3744
3745
        $query = new LocationQuery(
3746
            [
3747
                'filter' => new Criterion\ParentLocationId($designLocationId),
3748
                'offset' => 0,
3749
                'limit' => 10,
3750
                'sortClauses' => [
3751
                    new SortClause\Location\IsMainLocation(
3752
                        LocationQuery::SORT_DESC
3753
                    ),
3754
                ],
3755
            ]
3756
        );
3757
3758
        $searchService = $repository->getSearchService();
3759
        $result = $searchService->findLocations($query);
3760
3761
        $this->assertEquals(2, $result->totalCount);
3762
        $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...
3763
        $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...
3764
    }
3765
3766
    /**
3767
     * Test for the findLocations() method.
3768
     *
3769
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3770
     */
3771
    public function testContentWithMultipleLocations()
3772
    {
3773
        $repository = $this->getRepository();
3774
        $contentService = $repository->getContentService();
3775
        $contentTypeService = $repository->getContentTypeService();
3776
        $locationService = $repository->getLocationService();
3777
3778
        $forumType = $contentTypeService->loadContentTypeByIdentifier('forum');
3779
3780
        $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB');
3781
        $createStruct->alwaysAvailable = false;
3782
        $createStruct->setField('name', 'An awesome duplicate forum');
3783
3784
        $draft = $contentService->createContent($createStruct);
3785
        $content = $contentService->publishVersion($draft->getVersionInfo());
3786
3787
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
3788
        $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3789
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5);
3790
        $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3791
3792
        $this->refreshSearch($repository);
3793
3794
        $query = new LocationQuery(
3795
            [
3796
                'filter' => new Criterion\ContentId($content->id),
3797
                'sortClauses' => [
3798
                    new SortClause\Location\Id(LocationQuery::SORT_ASC),
3799
                ],
3800
            ]
3801
        );
3802
3803
        $searchService = $repository->getSearchService();
3804
        $result = $searchService->findLocations($query);
3805
3806
        $this->assertEquals(2, $result->totalCount);
3807
        $this->assertEquals(
3808
            $location1->id,
3809
            $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...
3810
        );
3811
        $this->assertEquals(
3812
            $location2->id,
3813
            $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...
3814
        );
3815
    }
3816
3817
    protected function createContentForTestUserMetadataGroupHorizontal()
3818
    {
3819
        $repository = $this->getRepository();
3820
        $contentService = $repository->getContentService();
3821
        $contentTypeService = $repository->getContentTypeService();
3822
        $locationService = $repository->getLocationService();
3823
        $userService = $repository->getUserService();
3824
        $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...
3825
        // ID of the "Administrators" user group in an eZ Publish demo installation
3826
        $administratorsUserGroupId = 12;
3827
        // ID of the "Editors" user group in an eZ Publish demo installation
3828
        $editorsUserGroupId = 13;
3829
3830
        $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId);
3831
        $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId);
3832
3833
        // Add additional Location for Administrators UserGroup under Editors UserGroup Location
3834
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3835
            $editorsUserGroup->contentInfo->mainLocationId
3836
        );
3837
        $newAdministratorsUserGroupLocation = $locationService->createLocation(
3838
            $administratorsUserGroup->contentInfo,
3839
            $locationCreateStruct
3840
        );
3841
3842
        // Add additional Location for administrator user under newly created UserGroup Location
3843
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3844
            $newAdministratorsUserGroupLocation->id
3845
        );
3846
        $locationService->createLocation(
3847
            $administratorUser->contentInfo,
3848
            $locationCreateStruct
3849
        );
3850
3851
        // Create a Content to be found through Editors UserGroup id.
3852
        // This ensures data is indexed, it could also be done by updating metadata of
3853
        // an existing Content, but slot would need to reindex Content and that should
3854
        // be tested elsewhere (dedicated indexing integration tests, missing ATM).
3855
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
3856
3857
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3858
        $createStruct->setField('name', 'test');
3859
3860
        $locationCreateStruct = $locationService->newLocationCreateStruct(2);
3861
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
3862
        $content = $contentService->publishVersion($draft->getVersionInfo());
3863
        $contentTypeService->createContentTypeDraft($contentType);
3864
3865
        $this->refreshSearch($repository);
3866
3867
        return $content;
3868
    }
3869
3870
    /**
3871
     * Test for the findContent() method.
3872
     *
3873
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3874
     */
3875
    public function testUserMetadataGroupHorizontalFilterContent($queryType = null)
3876
    {
3877
        if ($queryType === null) {
3878
            $queryType = 'filter';
3879
        }
3880
3881
        $repository = $this->getRepository();
3882
        $searchService = $repository->getSearchService();
3883
        $editorsUserGroupId = 13;
3884
3885
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3886
3887
        $criteria = [];
3888
        $setupFactory = $this->getSetupFactory();
3889
3890
        // Do not limit for LSE, as it does not not require reindexing.
3891
        // See explanation below.
3892
        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...
3893
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3894
        }
3895
3896
        $criteria[] = new Criterion\UserMetadata(
3897
            Criterion\UserMetadata::GROUP,
3898
            Criterion\Operator::EQ,
3899
            $editorsUserGroupId
3900
        );
3901
3902
        $query = new Query(
3903
            [
3904
                $queryType => new Criterion\LogicalAnd($criteria),
3905
                'sortClauses' => [
3906
                    new SortClause\ContentId(),
3907
                ],
3908
                'limit' => 50,
3909
            ]
3910
        );
3911
3912
        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...
3913
            $result = $searchService->findContent($query);
3914
3915
            // Administrator User is owned by itself, when additional Locations are added
3916
            // it should be reindexed and its UserGroups will updated, which means it should
3917
            // also be found as a Content of Editors UserGroup. However we do not handle this
3918
            // in slots yet, and also miss SPI methods to do it without using Search (also
3919
            // needed to decouple services), because as indexing is asynchronous Search
3920
            // should not eat its own dog food for reindexing.
3921
            $this->assertEquals(1, $result->totalCount);
3922
3923
            $this->assertEquals(
3924
                $content->id,
3925
                $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...
3926
            );
3927
        } else {
3928
            // This is how it should eventually work for all search engines,
3929
            // with required reindexing slots properly implemented.
3930
3931
            $result = $searchService->findContent($query);
3932
3933
            // Assert last hit manually, as id will change because it is created in test
3934
            // and not present it base fixture.
3935
            $foundContent1 = array_pop($result->searchHits);
3936
            $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...
3937
            $this->assertEquals($content->id, $foundContent1->valueObject->id);
3938
3939
            $this->simplifySearchResult($result);
3940
            $this->assertEquals(
3941
                include $this->getFixtureDir() . '/UserMetadata.php',
3942
                $result,
3943
                'Search results do not match.',
3944
                .1 // Be quite generous regarding delay -- most important for scores
3945
            );
3946
        }
3947
    }
3948
3949
    /**
3950
     * Test for the findContent() method.
3951
     *
3952
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3953
     */
3954
    public function testUserMetadataGroupHorizontalQueryContent()
3955
    {
3956
        $this->testUserMetadataGroupHorizontalFilterContent('query');
3957
    }
3958
3959
    /**
3960
     * Test for the findLocations() method.
3961
     *
3962
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3963
     */
3964
    public function testUserMetadataGroupHorizontalFilterLocation($queryType = null)
3965
    {
3966
        if ($queryType === null) {
3967
            $queryType = 'filter';
3968
        }
3969
3970
        $repository = $this->getRepository();
3971
        $searchService = $repository->getSearchService();
3972
        $editorsUserGroupId = 13;
3973
3974
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3975
3976
        $criteria = [];
3977
        $setupFactory = $this->getSetupFactory();
3978
3979
        // Do not limit for LSE, as it does not not require reindexing.
3980
        // See explanation below.
3981
        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...
3982
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3983
        }
3984
3985
        $criteria[] = new Criterion\UserMetadata(
3986
            Criterion\UserMetadata::GROUP,
3987
            Criterion\Operator::EQ,
3988
            $editorsUserGroupId
3989
        );
3990
3991
        $query = new LocationQuery(
3992
            [
3993
                $queryType => new Criterion\LogicalAnd($criteria),
3994
                'sortClauses' => [
3995
                    new SortClause\Location\Id(),
3996
                ],
3997
                'limit' => 50,
3998
            ]
3999
        );
4000
4001
        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...
4002
            $result = $searchService->findLocations($query);
4003
4004
            // Administrator User is owned by itself, when additional Locations are added
4005
            // it should be reindexed and its UserGroups will updated, which means it should
4006
            // also be found as a Content of Editors UserGroup. However we do not handle this
4007
            // in slots yet, and also miss SPI methods to do it without using Search (also
4008
            // needed to decouple services), because as indexing is asynchronous Search
4009
            // should not eat its own dog food for reindexing.
4010
            $this->assertEquals(1, $result->totalCount);
4011
4012
            $this->assertEquals(
4013
                $content->contentInfo->mainLocationId,
4014
                $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...
4015
            );
4016
        } else {
4017
            // This is how it should eventually work for all search engines,
4018
            // with required reindexing slots properly implemented.
4019
4020
            $result = $searchService->findLocations($query);
4021
4022
            // Assert last two hits manually, as ids will change because they are created
4023
            // in test and not present in base fixture.
4024
            $foundLocation1 = array_pop($result->searchHits);
4025
            $foundLocation2 = array_pop($result->searchHits);
4026
            // Remove additional Administrators UserGroup Location
4027
            array_pop($result->searchHits);
4028
            $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...
4029
            $this->assertEquals(
4030
                $content->versionInfo->contentInfo->mainLocationId,
4031
                $foundLocation1->valueObject->id
4032
            );
4033
            $this->assertEquals(
4034
                $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...
4035
                $foundLocation2->valueObject->contentId
4036
            );
4037
4038
            $this->simplifySearchResult($result);
4039
            $this->assertEquals(
4040
                include $this->getFixtureDir() . '/UserMetadataLocation.php',
4041
                $result,
4042
                'Search results do not match.',
4043
                .1 // Be quite generous regarding delay -- most important for scores
4044
            );
4045
        }
4046
    }
4047
4048
    /**
4049
     * Test for the findLocations() method.
4050
     *
4051
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4052
     */
4053
    public function testUserMetadataGroupHorizontalQueryLocation()
4054
    {
4055
        $this->testUserMetadataGroupHorizontalFilterLocation('query');
4056
    }
4057
4058
    /**
4059
     * Test for FullText on the findContent() method.
4060
     *
4061
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4062
     */
4063
    public function testFullTextOnNewContent()
4064
    {
4065
        $repository = $this->getRepository();
4066
        $contentService = $repository->getContentService();
4067
        $contentTypeService = $repository->getContentTypeService();
4068
        $locationService = $repository->getLocationService();
4069
        $searchService = $repository->getSearchService();
4070
4071
        $contentCreateStruct = $contentService->newContentCreateStruct(
4072
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4073
            'eng-GB'
4074
        );
4075
4076
        $contentCreateStruct->setField('name', 'foxes');
4077
4078
        $englishContent = $contentService->publishVersion(
4079
            $contentService->createContent(
4080
                $contentCreateStruct,
4081
                [$locationService->newLocationCreateStruct(2)]
4082
            )->versionInfo
4083
        );
4084
4085
        $this->refreshSearch($repository);
4086
4087
        $query = new Query(
4088
            [
4089
                'query' => new Criterion\FullText('foxes'),
4090
            ]
4091
        );
4092
4093
        $searchResult = $searchService->findContentInfo($query);
4094
4095
        $this->assertEquals(1, $searchResult->totalCount);
4096
        $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...
4097
    }
4098
4099
    /**
4100
     * Test for the findContent() method.
4101
     *
4102
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4103
     */
4104
    public function testLanguageAnalysisSeparateContent()
4105
    {
4106
        $setupFactory = $this->getSetupFactory();
4107
        if (!$setupFactory instanceof LegacyElasticsearch) {
4108
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4109
        }
4110
4111
        $repository = $this->getRepository();
4112
        $contentService = $repository->getContentService();
4113
        $contentTypeService = $repository->getContentTypeService();
4114
        $locationService = $repository->getLocationService();
4115
        $searchService = $repository->getSearchService();
4116
        $languageService = $repository->getContentLanguageService();
4117
4118
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4119
        $languageCreateStruct->languageCode = 'rus-RU';
4120
        $languageCreateStruct->name = 'Russian';
4121
4122
        $languageService->createLanguage($languageCreateStruct);
4123
4124
        $contentCreateStruct = $contentService->newContentCreateStruct(
4125
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4126
            'eng-GB'
4127
        );
4128
4129
        $contentCreateStruct->setField('name', 'foxes');
4130
4131
        $englishContent = $contentService->publishVersion(
4132
            $contentService->createContent(
4133
                $contentCreateStruct,
4134
                [$locationService->newLocationCreateStruct(2)]
4135
            )->versionInfo
4136
        );
4137
4138
        $contentCreateStruct = $contentService->newContentCreateStruct(
4139
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4140
            'rus-RU'
4141
        );
4142
4143
        $contentCreateStruct->setField('name', 'foxes');
4144
4145
        $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...
4146
            $contentService->createContent(
4147
                $contentCreateStruct,
4148
                [$locationService->newLocationCreateStruct(2)]
4149
            )->versionInfo
4150
        );
4151
4152
        // Only Content in English should be found, because Content in Russian
4153
        // will not be correctly stemmed
4154
        $query = new Query(
4155
            [
4156
                'query' => new Criterion\FullText('foxing'),
4157
            ]
4158
        );
4159
4160
        $searchResult = $searchService->findContent($query);
4161
4162
        $this->assertEquals(1, $searchResult->totalCount);
4163
        $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...
4164
    }
4165
4166
    /**
4167
     * Test for the findContent() method.
4168
     *
4169
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4170
     */
4171
    public function testLanguageAnalysisSameContent()
4172
    {
4173
        $setupFactory = $this->getSetupFactory();
4174
        if (!$setupFactory instanceof LegacyElasticsearch) {
4175
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4176
        }
4177
4178
        $repository = $this->getRepository();
4179
        $contentService = $repository->getContentService();
4180
        $contentTypeService = $repository->getContentTypeService();
4181
        $locationService = $repository->getLocationService();
4182
        $searchService = $repository->getSearchService();
4183
        $languageService = $repository->getContentLanguageService();
4184
4185
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4186
        $languageCreateStruct->languageCode = 'rus-RU';
4187
        $languageCreateStruct->name = 'Russian';
4188
4189
        $languageService->createLanguage($languageCreateStruct);
4190
4191
        $contentCreateStruct = $contentService->newContentCreateStruct(
4192
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4193
            'eng-GB'
4194
        );
4195
4196
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4197
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4198
4199
        $mixedContent = $contentService->publishVersion(
4200
            $contentService->createContent(
4201
                $contentCreateStruct,
4202
                [$locationService->newLocationCreateStruct(2)]
4203
            )->versionInfo
4204
        );
4205
4206
        // Content will be found because translation in Russian will be correctly stemmed
4207
        $query = new Query(
4208
            [
4209
                'query' => new Criterion\FullText('важнее'),
4210
            ]
4211
        );
4212
4213
        $searchResult = $searchService->findContent($query);
4214
4215
        $this->assertEquals(1, $searchResult->totalCount);
4216
        $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...
4217
    }
4218
4219
    /**
4220
     * Test for the findContent() method.
4221
     *
4222
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4223
     */
4224
    public function testLanguageAnalysisSameContentNotFound()
4225
    {
4226
        $setupFactory = $this->getSetupFactory();
4227
        if (!$setupFactory instanceof LegacyElasticsearch) {
4228
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4229
        }
4230
4231
        $repository = $this->getRepository();
4232
        $contentService = $repository->getContentService();
4233
        $contentTypeService = $repository->getContentTypeService();
4234
        $locationService = $repository->getLocationService();
4235
        $searchService = $repository->getSearchService();
4236
        $languageService = $repository->getContentLanguageService();
4237
4238
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4239
        $languageCreateStruct->languageCode = 'rus-RU';
4240
        $languageCreateStruct->name = 'Russian';
4241
4242
        $languageService->createLanguage($languageCreateStruct);
4243
4244
        $contentCreateStruct = $contentService->newContentCreateStruct(
4245
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4246
            'eng-GB'
4247
        );
4248
4249
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4250
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4251
4252
        $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...
4253
            $contentService->createContent(
4254
                $contentCreateStruct,
4255
                [$locationService->newLocationCreateStruct(2)]
4256
            )->versionInfo
4257
        );
4258
4259
        // Content should be found because translation in Russian will be correctly stemmed
4260
        $query = new Query(
4261
            [
4262
                'query' => new Criterion\FullText('важнее'),
4263
            ]
4264
        );
4265
4266
        // Filtering fields for only English will cause no match because the term will
4267
        // not be correctly stemmed
4268
        $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]);
4269
4270
        $this->assertEquals(0, $searchResult->totalCount);
4271
    }
4272
4273
    /**
4274
     * Test for the findContent() method searching for content filtered by languages.
4275
     *
4276
     * @covers \eZ\Publish\Core\Repository\SearchService::findContent
4277
     */
4278
    public function testFindContentWithLanguageFilter()
4279
    {
4280
        $repository = $this->getRepository();
4281
        $searchService = $repository->getSearchService();
4282
4283
        $query = new Query(
4284
            [
4285
                'filter' => new Criterion\ContentId([4]),
4286
                'offset' => 0,
4287
            ]
4288
        );
4289
        $searchResult = $searchService->findContent(
4290
            $query,
4291
            ['languages' => ['eng-US']],
4292
            false
4293
        );
4294
        /* END: Use Case */
4295
4296
        $this->assertInstanceOf(
4297
            SearchResult::class,
4298
            $searchResult
4299
        );
4300
4301
        $this->assertEquals(1, $searchResult->totalCount);
4302
        $this->assertCount($searchResult->totalCount, $searchResult->searchHits);
4303
        foreach ($searchResult->searchHits as $searchHit) {
4304
            $this->assertInstanceOf(
4305
                SearchHit::class,
4306
                $searchHit
4307
            );
4308
        }
4309
    }
4310
4311
    /**
4312
     * This test prepares data for other tests.
4313
     *
4314
     * @see testFulltextContentSearchComplex
4315
     * @see testFulltextLocationSearchComplex
4316
     *
4317
     * @return array
4318
     */
4319
    public function testFulltextComplex()
4320
    {
4321
        $repository = $this->getRepository();
4322
        $contentService = $repository->getContentService();
4323
        $contentTypeService = $repository->getContentTypeService();
4324
        $locationService = $repository->getLocationService();
4325
4326
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
4327
        $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4328
4329
        $contentCreateStruct->setField('name', 'red');
4330
        $contentCreateStruct->setField('short_name', 'red apple');
4331
        $content1 = $contentService->publishVersion(
4332
            $contentService->createContent(
4333
                $contentCreateStruct,
4334
                [$locationService->newLocationCreateStruct(2)]
4335
            )->versionInfo
4336
        );
4337
4338
        $contentCreateStruct->setField('name', 'apple');
4339
        $contentCreateStruct->setField('short_name', 'two');
4340
        $content2 = $contentService->publishVersion(
4341
            $contentService->createContent(
4342
                $contentCreateStruct,
4343
                [$locationService->newLocationCreateStruct(2)]
4344
            )->versionInfo
4345
        );
4346
4347
        $contentCreateStruct->setField('name', 'red apple');
4348
        $contentCreateStruct->setField('short_name', 'three');
4349
        $content3 = $contentService->publishVersion(
4350
            $contentService->createContent(
4351
                $contentCreateStruct,
4352
                [$locationService->newLocationCreateStruct(2)]
4353
            )->versionInfo
4354
        );
4355
4356
        $contentCreateStruct->setField('name', 'four');
4357
        $contentCreateStruct->setField('name', 'german red apple', 'ger-DE');
4358
        $contentCreateStruct->setField('short_name', 'four');
4359
        $contentCreateStruct->setField('short_name', 'german red apple', 'ger-DE');
4360
        $contentService->publishVersion(
4361
            $contentService->createContent(
4362
                $contentCreateStruct,
4363
                [$locationService->newLocationCreateStruct(2)]
4364
            )->versionInfo
4365
        );
4366
4367
        $this->refreshSearch($repository);
4368
4369
        $criterion = new Criterion\FullText(
4370
            'red apple',
4371
            [
4372
                'boost' => [
4373
                    'short_name' => 2,
4374
                ],
4375
                'fuzziness' => .1,
4376
            ]
4377
        );
4378
4379
        return [$criterion, $content1, $content2, $content3];
4380
    }
4381
4382
    /**
4383
     * Test for the findContent() method.
4384
     *
4385
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4386
     * @depends testFulltextComplex
4387
     *
4388
     * @param array $data
4389
     */
4390
    public function testFulltextContentSearchComplex(array $data)
4391
    {
4392
        // Do not initialize from scratch
4393
        $repository = $this->getRepository(false);
4394
        $searchService = $repository->getSearchService();
4395
        list($criterion, $content1, $content2, $content3) = $data;
4396
4397
        $searchResult = $searchService->findContent(
4398
            new Query(['query' => $criterion]),
4399
            ['languages' => ['eng-GB']]
4400
        );
4401
        $searchHits = $searchResult->searchHits;
4402
4403
        $this->assertEquals(3, $searchResult->totalCount);
4404
4405
        // Legacy search engine does have scoring, sorting the results by ID in that case
4406
        $setupFactory = $this->getSetupFactory();
4407 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...
4408
            $this->sortSearchHitsById($searchHits);
4409
4410
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
4411
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->id);
4412
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->id);
4413
4414
            return;
4415
        }
4416
4417
        // Assert scores are descending
4418
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4419
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4420
4421
        // Assert order
4422
        $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...
4423
        $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...
4424
        $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...
4425
    }
4426
4427
    /**
4428
     * Test for the findContent() method.
4429
     *
4430
     * @covers \eZ\Publish\API\Repository\SearchService::findContent()
4431
     * @depends testFulltextComplex
4432
     *
4433
     * @param array $data
4434
     */
4435
    public function testFulltextContentTranslationSearch(array $data)
4436
    {
4437
        $criterion = $data[0];
4438
        $query = new Query(['query' => $criterion]);
4439
4440
        $this->assertFulltextSearchForTranslations(self::FIND_CONTENT_METHOD, $query);
4441
    }
4442
4443
    /**
4444
     * Test for the findLocations() method.
4445
     *
4446
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4447
     * @depends testFulltextComplex
4448
     *
4449
     * @param array $data
4450
     */
4451
    public function testFulltextLocationSearchComplex(array $data)
4452
    {
4453
        $setupFactory = $this->getSetupFactory();
4454
        if ($setupFactory instanceof LegacyElasticsearch) {
4455
            $this->markTestIncomplete(
4456
                'Fulltext criterion is not supported with Location search in Elasticsearch engine'
4457
            );
4458
        }
4459
4460
        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...
4461
            $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!');
4462
        }
4463
4464
        // Do not initialize from scratch
4465
        $repository = $this->getRepository(false);
4466
        list($criterion, $content1, $content2, $content3) = $data;
4467
        $searchService = $repository->getSearchService();
4468
4469
        $searchResult = $searchService->findLocations(
4470
            new LocationQuery(['query' => $criterion]),
4471
            ['languages' => ['eng-GB']]
4472
        );
4473
        $searchHits = $searchResult->searchHits;
4474
4475
        $this->assertEquals(3, $searchResult->totalCount);
4476
4477
        // Legacy search engine does have scoring, sorting the results by ID in that case
4478
        $setupFactory = $this->getSetupFactory();
4479 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...
4480
            $this->sortSearchHitsById($searchHits);
4481
4482
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
4483
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId);
4484
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId);
4485
4486
            return;
4487
        }
4488
4489
        // Assert scores are descending
4490
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4491
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4492
4493
        // Assert order
4494
        $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...
4495
        $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...
4496
        $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...
4497
    }
4498
4499
    /**
4500
     * Test for the findLocations() method.
4501
     * @covers \eZ\Publish\API\Repository\SearchService::findLocations()
4502
     * @depends testFulltextComplex
4503
     *
4504
     * @param array $data
4505
     */
4506
    public function testFulltextLocationTranslationSearch(array $data): void
4507
    {
4508
        $criterion = $data[0];
4509
        $query = new LocationQuery(['query' => $criterion]);
4510
4511
        $this->assertFulltextSearchForTranslations(self::FIND_LOCATION_METHOD, $query);
4512
    }
4513
4514
    /**
4515
     * Assert that query result matches the given fixture.
4516
     *
4517
     * @param Query $query
4518
     * @param string $fixture
4519
     * @param null|callable $closure
4520
     * @param bool $info
4521
     * @param bool $id
4522
     */
4523
    protected function assertQueryFixture(Query $query, $fixture, $closure = null, $ignoreScore = true, $info = false, $id = true)
4524
    {
4525
        $repository = $this->getRepository();
4526
        $searchService = $repository->getSearchService();
4527
4528
        try {
4529
            if ($query instanceof LocationQuery) {
4530
                $setupFactory = $this->getSetupFactory();
4531
                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...
4532
                    // @todo When we want to test score again by default we will need fixtures for Solr
4533
                }
4534
4535
                if ($setupFactory instanceof LegacyElasticsearch) {
4536
                    $position = strrpos($fixture, '/');
4537
                    $fixture = substr_replace($fixture, '/Location', $position, 0);
4538
                }
4539
4540
                $result = $searchService->findLocations($query);
4541
            } elseif ($query instanceof Query) {
4542
                if ($info) {
4543
                    $result = $searchService->findContentInfo($query);
4544
                } else {
4545
                    $result = $searchService->findContent($query);
4546
                }
4547
            } else {
4548
                $this->fail('Expected instance of LocationQuery or Query, got: ' . gettype($query));
4549
            }
4550
            $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...
4551
        } catch (NotImplementedException $e) {
4552
            $this->markTestSkipped(
4553
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4554
            );
4555
        }
4556
4557 View Code Duplication
        if (!is_file($fixture)) {
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...
4558
            if (isset($_ENV['ez_tests_record'])) {
4559
                file_put_contents(
4560
                    $record = $fixture . '.recording',
4561
                    "<?php\n\nreturn " . var_export($result, true) . ";\n\n"
4562
                );
4563
                $this->markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result));
4564
            } else {
4565
                $this->markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixture);
4566
            }
4567
        }
4568
4569
        $fixture = include $fixture;
4570
4571
        if ($closure !== null) {
4572
            $closure($fixture);
4573
            $closure($result);
4574
        }
4575
4576 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...
4577
            foreach ([$fixture, $result] as $set) {
4578
                $property = new \ReflectionProperty(get_class($set), 'maxScore');
4579
                $property->setAccessible(true);
4580
                $property->setValue($set, 0.0);
4581
4582
                foreach ($set->searchHits as $hit) {
4583
                    $property = new \ReflectionProperty(get_class($hit), 'score');
4584
                    $property->setAccessible(true);
4585
                    $property->setValue($hit, 0.0);
4586
                }
4587
            }
4588
        }
4589
4590
        foreach ([$fixture, $result] as $set) {
4591
            foreach ($set->searchHits as $hit) {
4592
                $property = new \ReflectionProperty(get_class($hit), 'index');
4593
                $property->setAccessible(true);
4594
                $property->setValue($hit, null);
4595
4596
                $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation');
4597
                $property->setAccessible(true);
4598
                $property->setValue($hit, null);
4599
4600
                if (!$id) {
4601
                    $hit->valueObject['id'] = null;
4602
                }
4603
            }
4604
        }
4605
4606
        $this->assertEquals(
4607
            $fixture,
4608
            $result,
4609
            'Search results do not match.',
4610
            .99 // Be quite generous regarding delay -- most important for scores
4611
        );
4612
    }
4613
4614
    /**
4615
     * Show a simplified view of the search result for manual introspection.
4616
     *
4617
     * @param SearchResult $result
4618
     *
4619
     * @return string
4620
     */
4621 View Code Duplication
    protected function printResult(SearchResult $result)
4622
    {
4623
        $printed = '';
4624
        foreach ($result->searchHits as $hit) {
4625
            $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']);
4626
        }
4627
4628
        return $printed;
4629
    }
4630
4631
    /**
4632
     * Simplify search result.
4633
     *
4634
     * This leads to saner comparisons of results, since we do not get the full
4635
     * content objects every time.
4636
     *
4637
     * @param SearchResult $result
4638
     */
4639
    protected function simplifySearchResult(SearchResult $result)
4640
    {
4641
        $result->time = 1;
4642
4643
        foreach ($result->searchHits as $hit) {
4644
            switch (true) {
4645
                case $hit->valueObject instanceof Content:
4646 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...
4647
                    $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...
4648
                        'id' => $hit->valueObject->contentInfo->id,
4649
                        'title' => $hit->valueObject->contentInfo->name,
4650
                    ];
4651
                    break;
4652
4653
                case $hit->valueObject instanceof ContentInfo:
4654
                    $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...
4655
                        'id' => $hit->valueObject->id,
4656
                        'title' => $hit->valueObject->name,
4657
                    ];
4658
                    break;
4659
4660
                default:
4661
                    throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject));
4662
            }
4663
        }
4664
    }
4665
4666
    /**
4667
     * Get fixture directory.
4668
     *
4669
     * @return string
4670
     */
4671
    protected function getFixtureDir()
4672
    {
4673
        return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/';
4674
    }
4675
4676
    /**
4677
     * For findContentInfo tests, to reuse fixtures for findContent tests.
4678
     *
4679
     * @param null|callable $closure
4680
     *
4681
     * @return callable
4682
     */
4683
    private function getContentInfoFixtureClosure($closure = null)
4684
    {
4685
        /** @var $data \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */
4686
        return function (&$data) use ($closure) {
4687
            foreach ($data->searchHits as $searchHit) {
4688
                if ($searchHit->valueObject instanceof Content) {
4689
                    $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo();
4690
                }
4691
            }
4692
4693
            if (isset($closure)) {
4694
                $closure($data);
4695
            }
4696
        };
4697
    }
4698
4699
    /**
4700
     * Test searching using Field Criterion where the given Field Identifier exists in
4701
     * both searchable and non-searchable Fields.
4702
     * Number of returned results depends on used storage.
4703
     *
4704
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4705
     */
4706
    public function testFieldCriterionForContentsWithIdenticalFieldIdentifiers()
4707
    {
4708
        $this->createContentWithFieldType(
4709
            'url',
4710
            'title',
4711
            'foo'
4712
        );
4713
        $this->createContentWithFieldType(
4714
            'string',
4715
            'title',
4716
            'foo'
4717
        );
4718
        $query = new Query(
4719
            [
4720
                'query' => new Criterion\Field(
4721
                    'title',
4722
                    Criterion\Operator::EQ,
4723
                    'foo'
4724
                ),
4725
            ]
4726
        );
4727
4728
        $repository = $this->getRepository();
4729
        $searchService = $repository->getSearchService();
4730
        $result = $searchService->findContent($query);
4731
4732
        $this->assertTrue(($result->totalCount === 1 || $result->totalCount === 2));
4733
    }
4734
4735
    private function createContentWithFieldType(
4736
        string $fieldType,
4737
        string $fieldName,
4738
        string $fieldValue
4739
    ) {
4740
        $repository = $this->getRepository();
4741
        $contentTypeService = $repository->getContentTypeService();
4742
        $contentService = $repository->getContentService();
4743
4744
        $createStruct = $contentTypeService->newContentTypeCreateStruct($fieldType . uniqid());
4745
        $createStruct->mainLanguageCode = 'eng-GB';
4746
        $createStruct->remoteId = $fieldType . '-123';
4747
        $createStruct->names = ['eng-GB' => $fieldType];
4748
        $createStruct->creatorId = 14;
4749
        $createStruct->creationDate = new \DateTime();
4750
4751
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct($fieldName, 'ez' . $fieldType);
4752
        $fieldCreate->names = ['eng-GB' => $fieldName];
4753
        $fieldCreate->fieldGroup = 'main';
4754
        $fieldCreate->position = 1;
4755
4756
        $createStruct->addFieldDefinition($fieldCreate);
4757
4758
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
4759
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
4760
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
4761
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
4762
4763
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4764
        $createStruct->remoteId = $fieldType . '-456';
4765
        $createStruct->alwaysAvailable = false;
4766
        $createStruct->setField(
4767
            $fieldName,
4768
            $fieldValue
4769
        );
4770
4771
        $draft = $contentService->createContent($createStruct);
4772
        $content = $contentService->publishVersion($draft->getVersionInfo());
4773
4774
        $this->refreshSearch($repository);
4775
4776
        return $content;
4777
    }
4778
4779
    /**
4780
     * @param string $findMethod
4781
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
4782
     * @param array $languages
4783
     * @param bool $useAlwaysAvailable
4784
     *
4785
     * @throws \InvalidArgumentException
4786
     *
4787
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
4788
     */
4789
    private function find(
4790
        string $findMethod,
4791
        Query $query,
4792
        array $languages,
4793
        bool $useAlwaysAvailable
4794
    ): SearchResult {
4795
        if (false === in_array($findMethod, self::AVAILABLE_FIND_METHODS, true)) {
4796
            throw new InvalidArgumentException(
4797
                'Allowed find methods are: '
4798
                . implode(',', self::AVAILABLE_FIND_METHODS)
4799
            );
4800
        }
4801
4802
        $repository = $this->getRepository(false);
4803
        $searchService = $repository->getSearchService();
4804
4805
        return $searchService->{$findMethod}(
4806
            $query,
4807
            [
4808
                'languages' => $languages,
4809
                'useAlwaysAvailable' => $useAlwaysAvailable,
4810
            ]
4811
        );
4812
    }
4813
4814
    /**
4815
     * @param string $findMethod
4816
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
4817
     */
4818
    private function assertFulltextSearchForTranslations(string $findMethod, Query $query): void
4819
    {
4820
        /*
4821
         * Search in German translations without always available
4822
         */
4823
        $searchResult = $this->find($findMethod, $query, ['ger-DE'], false);
4824
        $this->assertEquals(1, $searchResult->totalCount);
4825
        $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']);
4826
4827
        /*
4828
         * Search in German translations with always available
4829
         */
4830
        $searchResult = $this->find($findMethod, $query, ['ger-DE'], true);
4831
        $this->assertEquals(4, $searchResult->totalCount);
4832
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']);
4833
4834
        /*
4835
         * Search in multiple (ger-DE, eng-GB) translations without always available
4836
         */
4837
        $searchResult = $this->find($findMethod, $query, ['ger-DE', 'eng-GB'], false);
4838
        $this->assertEquals(4, $searchResult->totalCount);
4839
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']);
4840
4841
        /*
4842
         * Search in multiple (eng-US, ger-DE) translations without always available
4843
         */
4844
        $searchResult = $this->find($findMethod, $query, ['eng-US', 'ger-DE'], false);
4845
        $this->assertEquals(1, $searchResult->totalCount);
4846
        $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']);
4847
4848
        /*
4849
         * Search in eng-US translations without always available
4850
         */
4851
        $searchResult = $this->find($findMethod, $query, ['eng-US'], false);
4852
        $this->assertEquals(0, $searchResult->totalCount);
4853
4854
        /*
4855
         * Search in eng-US translations with always available
4856
         */
4857
        $searchResult = $this->find($findMethod, $query, ['eng-US'], true);
4858
        $this->assertEquals(3, $searchResult->totalCount);
4859
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB']);
4860
    }
4861
4862
    /**
4863
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
4864
     * @param string[] $translationsToMatch
4865
     *
4866
     * @throws \InvalidArgumentException
4867
     */
4868
    private function assertSearchResultMatchTranslations(
4869
        SearchResult $searchResult,
4870
        array $translationsToMatch
4871
    ): void {
4872
        $translationsToMatchCount = count($translationsToMatch);
4873
4874
        if ($searchResult->totalCount < $translationsToMatchCount
4875
            || $searchResult->totalCount > $translationsToMatchCount
4876
        ) {
4877
            throw new InvalidArgumentException(
4878
                'Argument `translationsToMatch` must be equal to the search result total count!'
4879
            );
4880
        }
4881
4882
        $searchHits = $searchResult->searchHits;
4883
        $this->sortSearchHitsById($searchHits);
4884
4885
        for ($i = 0; $i < $searchResult->totalCount; ++$i) {
4886
            $this->assertEquals(
4887
                $translationsToMatch[$i],
4888
                $searchHits[$i]->matchedTranslation
4889
            );
4890
        }
4891
    }
4892
4893
    private function sortSearchHitsById(array &$searchHits): void
4894
    {
4895
        usort(
4896
           $searchHits,
4897
           static function (SearchHit $a, SearchHit $b): int {
4898
               return $a->valueObject->id <=> $b->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...
4899
           }
4900
        );
4901
    }
4902
}
4903