Completed
Push — 7.5 ( d3ada0...30de84 )
by
unknown
18:51 queued 12s
created

SearchServiceTest   F

Complexity

Total Complexity 172

Size/Duplication

Total Lines 4975
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 66

Importance

Changes 0
Metric Value
dl 0
loc 4975
rs 0.8
c 0
b 0
f 0
wmc 172
lcom 1
cbo 66

101 Methods

Rating   Name   Duplication   Size   Complexity  
B getFilterContentSearches() 0 550 5
B getContentQuerySearches() 0 74 1
B getLocationQuerySearches() 0 69 1
A testFindContentFiltered() 0 5 1
A testFindContentInfoFiltered() 0 5 1
A testFindLocationsContentFiltered() 0 5 1
A testDeprecatedCriteriaProperty() 0 14 1
A testQueryContent() 0 5 1
A getFixtureDir() 0 4 1
A getContentInfoFixtureClosure() 0 15 4
A testFieldCriterionForContentsWithIdenticalFieldIdentifiers() 0 28 2
A createContentWithFieldType() 0 43 1
A find() 0 24 2
A assertFulltextSearchForTranslations() 0 43 1
A assertSearchResultMatchTranslations() 0 24 4
A sortSearchHitsById() 0 9 1
A testQueryContentInfo() 0 5 1
A testQueryContentLocations() 0 5 1
A testQueryLocations() 0 5 1
A getCaseInsensitiveSearches() 0 35 1
A testFindContentFieldFiltersCaseSensitivity() 0 8 1
A testFindLocationsFieldFiltersCaseSensitivity() 0 8 1
B getRelationFieldFilterSearches() 0 73 1
A testRelationContentCreation() 0 23 1
A testFindRelationFieldContentInfoFiltered() 0 6 1
A testFindRelationFieldLocationsFiltered() 0 6 1
A testFindSingle() 0 16 1
A testFindNoPerformCount() 0 24 2
A testFindNoPerformCountException() 0 18 2
A testFindLocationsNoPerformCount() 0 24 2
A testFindLocationsNoPerformCountException() 0 18 2
B createMovieContent() 0 68 1
A createMultipleCountriesContent() 0 43 1
A testFieldIsEmpty() 0 27 1
A testFieldIsNotEmpty() 0 23 1
A testFieldCollectionContains() 0 24 1
A testFieldCollectionContainsNoMatch() 0 19 1
A testInvalidFieldIdentifierRange() 0 18 1
A testInvalidFieldIdentifierIn() 0 18 1
A testFindContentWithNonSearchableField() 0 18 1
A testSortFieldWithNonSearchableField() 0 13 1
A testSortMapLocationDistanceWithNonSearchableField() 0 20 1
A testFindSingleFailMultiple() 0 11 1
A testFindSingleWithNonSearchableField() 0 13 1
B getSortedContentSearches() 0 147 2
B getSortedLocationSearches() 0 72 2
A createTestContentType() 0 36 1
A createMultilingualContent() 0 30 3
A checkPrioritizedLanguagesSupport() 0 7 2
B providerForTestMultilingualFieldSort() 0 305 1
A testMultilingualFieldSortContent() 0 13 1
A testMultilingualFieldSortLocation() 0 14 1
B assertMultilingualFieldSort() 0 71 4
B providerForTestMultilingualFieldFilter() 0 158 1
A testMultilingualFieldFilterContent() 0 13 1
A testMultilingualFieldFilterLocation() 0 14 1
B assertMultilingualFieldFilter() 0 76 4
A mapResultContentIds() 0 13 2
A testFindAndSortContent() 0 5 1
A testFindAndSortContentInfo() 0 5 1
A testFindAndSortContentLocations() 0 5 1
A testFindAndSortLocations() 0 5 1
A testFindFacetedContent() 0 4 1
A testFindFacetedContentInfo() 0 4 1
A testQueryCustomField() 0 19 1
A testQueryModifiedField() 0 29 2
A testSortModifiedField() 0 30 2
A createTestPlaceContentType() 0 27 1
B testMapLocationDistanceLessThanOrEqual() 0 73 1
B testMapLocationDistanceGreaterThanOrEqual() 0 73 1
B testMapLocationDistanceBetween() 0 89 1
B testMapLocationDistanceBetweenPolar() 0 57 1
B testMapLocationDistanceSortAscending() 0 110 1
B testMapLocationDistanceSortDescending() 0 110 1
B testMapLocationDistanceWithCustomField() 0 81 2
B testMapLocationDistanceWithCustomFieldSort() 0 118 2
A testFindMainLocation() 0 39 1
A testFindNonMainLocation() 0 38 1
A testSortMainLocationAscending() 0 37 1
A testSortMainLocationDescending() 0 37 1
A testContentWithMultipleLocations() 0 42 1
A createContentForTestUserMetadataGroupHorizontal() 0 52 1
B testUserMetadataGroupHorizontalFilterContent() 0 73 6
A testUserMetadataGroupHorizontalQueryContent() 0 4 1
B testUserMetadataGroupHorizontalFilterLocation() 0 83 6
A testUserMetadataGroupHorizontalQueryLocation() 0 4 1
A testFullTextOnNewContent() 0 35 1
B testLanguageAnalysisSeparateContent() 0 61 2
A testLanguageAnalysisSameContent() 0 47 2
A testLanguageAnalysisSameContentNotFound() 0 48 2
A testFindContentWithLanguageFilter() 0 32 2
B testFulltextComplex() 0 62 1
A testFulltextContentSearchComplex() 0 36 2
A testFulltextContentTranslationSearch() 0 7 1
B testFulltextLocationSearchComplex() 0 47 5
A testFulltextLocationTranslationSearch() 0 7 1
F assertQueryFixture() 0 96 16
A printResult() 0 9 2
A simplifySearchResult() 0 26 5
A testSortingByNumericFieldsWithValuesOfDifferentLength() 0 21 1
B providerForTestSortingByNumericFieldsWithValuesOfDifferentLength() 0 65 1

How to fix   Complexity   

Complex Class

Complex classes like SearchServiceTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SearchServiceTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\API\Repository\Tests;
8
9
use eZ\Publish\API\Repository\Tests\SetupFactory\LegacyElasticsearch;
10
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
11
use InvalidArgumentException;
12
use eZ\Publish\API\Repository\Values\Content\Content;
13
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
14
use eZ\Publish\API\Repository\Values\Content\Query;
15
use eZ\Publish\API\Repository\Values\Content\Location;
16
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
17
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
18
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
19
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
20
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
21
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
22
use function count;
23
24
/**
25
 * Test case for operations in the SearchService.
26
 *
27
 * @see eZ\Publish\API\Repository\SearchService
28
 * @group integration
29
 * @group search
30
 */
31
class SearchServiceTest extends BaseTest
32
{
33
    const QUERY_CLASS = Query::class;
34
35
    const FIND_CONTENT_METHOD = 'findContent';
36
    const FIND_LOCATION_METHOD = 'findLocations';
37
38
    const AVAILABLE_FIND_METHODS = [
39
        self::FIND_CONTENT_METHOD,
40
        self::FIND_LOCATION_METHOD,
41
    ];
42
43
    use Common\FacetedSearchProvider;
44
45
    public function getFilterContentSearches()
46
    {
47
        $fixtureDir = $this->getFixtureDir();
48
49
        return [
50
            0 => [
51
                [
52
                    'filter' => new Criterion\ContentId(
53
                        [1, 4, 10]
54
                    ),
55
                    'sortClauses' => [new SortClause\ContentId()],
56
                ],
57
                $fixtureDir . 'ContentId.php',
58
            ],
59
            1 => [
60
                [
61
                    'filter' => new Criterion\LogicalAnd(
62
                        [
63
                            new Criterion\ContentId(
64
                                [1, 4, 10]
65
                            ),
66
                            new Criterion\ContentId(
67
                                [4, 12]
68
                            ),
69
                        ]
70
                    ),
71
                    'sortClauses' => [new SortClause\ContentId()],
72
                ],
73
                $fixtureDir . 'LogicalAnd.php',
74
            ],
75
            2 => [
76
                [
77
                    'filter' => new Criterion\LogicalOr(
78
                        [
79
                            new Criterion\ContentId(
80
                                [1, 4, 10]
81
                            ),
82
                            new Criterion\ContentId(
83
                                [4, 12]
84
                            ),
85
                        ]
86
                    ),
87
                    'sortClauses' => [new SortClause\ContentId()],
88
                ],
89
                $fixtureDir . 'LogicalOr.php',
90
            ],
91
            3 => [
92
                [
93
                    'filter' => new Criterion\LogicalAnd(
94
                        [
95
                            new Criterion\ContentId(
96
                                [1, 4, 10]
97
                            ),
98
                            new Criterion\LogicalNot(
99
                                new Criterion\ContentId(
100
                                    [10, 12]
101
                                )
102
                            ),
103
                        ]
104
                    ),
105
                    'sortClauses' => [new SortClause\ContentId()],
106
                ],
107
                $fixtureDir . 'LogicalNot.php',
108
            ],
109
            4 => [
110
                [
111
                    'filter' => new Criterion\LogicalAnd(
112
                        [
113
                            new Criterion\ContentId(
114
                                [1, 4, 10]
115
                            ),
116
                            new Criterion\LogicalAnd(
117
                                [
118
                                    new Criterion\LogicalNot(
119
                                        new Criterion\ContentId(
120
                                            [10, 12]
121
                                        )
122
                                    ),
123
                                ]
124
                            ),
125
                        ]
126
                    ),
127
                    'sortClauses' => [new SortClause\ContentId()],
128
                ],
129
                $fixtureDir . 'LogicalNot.php',
130
            ],
131
            5 => [
132
                [
133
                    'filter' => new Criterion\ContentTypeId(
134
                        4
135
                    ),
136
                    'sortClauses' => [new SortClause\ContentId()],
137
                ],
138
                $fixtureDir . 'ContentTypeId.php',
139
            ],
140
            6 => [
141
                [
142
                    'filter' => new Criterion\ContentTypeIdentifier(
143
                        'user'
144
                    ),
145
                    'sortClauses' => [new SortClause\ContentId()],
146
                ],
147
                $fixtureDir . 'ContentTypeId.php',
148
            ],
149
            7 => [
150
                [
151
                    'filter' => new Criterion\ContentTypeIdentifier(
152
                        'user',
153
                        'invalid'
154
                    ),
155
                    'sortClauses' => [new SortClause\ContentId()],
156
                ],
157
                $fixtureDir . 'ContentTypeId.php',
158
            ],
159
            8 => [
160
                [
161
                    'filter' => new Criterion\ContentTypeIdentifier(
162
                        'invalid1',
163
                        'invalid2'
164
                    ),
165
                    'sortClauses' => [new SortClause\ContentId()],
166
                ],
167
                $fixtureDir . 'MatchNone.php',
168
            ],
169
            9 => [
170
                [
171
                    'filter' => new Criterion\MatchNone(),
172
                    'sortClauses' => [new SortClause\ContentId()],
173
                ],
174
                $fixtureDir . 'MatchNone.php',
175
            ],
176
            10 => [
177
                [
178
                    'filter' => new Criterion\ContentTypeGroupId(
179
                        2
180
                    ),
181
                    'sortClauses' => [new SortClause\ContentId()],
182
                ],
183
                $fixtureDir . 'ContentTypeGroupId.php',
184
            ],
185
            11 => [
186
                [
187
                    'filter' => new Criterion\DateMetadata(
188
                        Criterion\DateMetadata::MODIFIED,
189
                        Criterion\Operator::GT,
190
                        1343140540
191
                    ),
192
                    'sortClauses' => [new SortClause\ContentId()],
193
                ],
194
                $fixtureDir . 'DateMetadataGt.php',
195
            ],
196
            12 => [
197
                [
198
                    'filter' => new Criterion\DateMetadata(
199
                        Criterion\DateMetadata::MODIFIED,
200
                        Criterion\Operator::GTE,
201
                        1311154215
202
                    ),
203
                    'sortClauses' => [new SortClause\ContentId()],
204
                ],
205
                $fixtureDir . 'DateMetadataGte.php',
206
            ],
207
            13 => [
208
                [
209
                    'filter' => new Criterion\DateMetadata(
210
                        Criterion\DateMetadata::MODIFIED,
211
                        Criterion\Operator::LTE,
212
                        1311154215
213
                    ),
214
                    'limit' => 10,
215
                    'sortClauses' => [new SortClause\ContentId()],
216
                ],
217
                $fixtureDir . 'DateMetadataLte.php',
218
            ],
219
            14 => [
220
                [
221
                    'filter' => new Criterion\DateMetadata(
222
                        Criterion\DateMetadata::MODIFIED,
223
                        Criterion\Operator::IN,
224
                        [1033920794, 1060695457, 1343140540]
225
                    ),
226
                    'sortClauses' => [new SortClause\ContentId()],
227
                ],
228
                $fixtureDir . 'DateMetadataIn.php',
229
            ],
230
            15 => [
231
                [
232
                    'filter' => new Criterion\DateMetadata(
233
                        Criterion\DateMetadata::MODIFIED,
234
                        Criterion\Operator::BETWEEN,
235
                        [1033920776, 1072180276]
236
                    ),
237
                    'sortClauses' => [new SortClause\ContentId()],
238
                ],
239
                $fixtureDir . 'DateMetadataBetween.php',
240
            ],
241
            16 => [
242
                [
243
                    'filter' => new Criterion\DateMetadata(
244
                        Criterion\DateMetadata::CREATED,
245
                        Criterion\Operator::BETWEEN,
246
                        [1033920776, 1072180278]
247
                    ),
248
                    'sortClauses' => [new SortClause\ContentId()],
249
                ],
250
                $fixtureDir . 'DateMetadataCreated.php',
251
            ],
252
            17 => [
253
                [
254
                    'filter' => new Criterion\CustomField(
255
                        'user_group_name_value_s',
256
                        Criterion\Operator::EQ,
257
                        'Members'
258
                    ),
259
                    'sortClauses' => [new SortClause\ContentId()],
260
                ],
261
                $fixtureDir . 'Field.php',
262
            ],
263
            18 => [
264
                [
265
                    'filter' => new Criterion\CustomField(
266
                        'user_group_name_value_s',
267
                        Criterion\Operator::CONTAINS,
268
                        'Members'
269
                    ),
270
                    'sortClauses' => [new SortClause\ContentId()],
271
                ],
272
                $fixtureDir . 'Field.php',
273
            ],
274
            19 => [
275
                [
276
                    'filter' => new Criterion\CustomField(
277
                        'user_group_name_value_s',
278
                        Criterion\Operator::LT,
279
                        'Members'
280
                    ),
281
                    'sortClauses' => [new SortClause\ContentId()],
282
                ],
283
                $fixtureDir . 'CustomFieldLt.php',
284
            ],
285
            20 => [
286
                [
287
                    'filter' => new Criterion\CustomField(
288
                        'user_group_name_value_s',
289
                        Criterion\Operator::LTE,
290
                        'Members'
291
                    ),
292
                    'sortClauses' => [new SortClause\ContentId()],
293
                ],
294
                $fixtureDir . 'CustomFieldLte.php',
295
            ],
296
            21 => [
297
                [
298
                    'filter' => new Criterion\CustomField(
299
                        'user_group_name_value_s',
300
                        Criterion\Operator::GT,
301
                        'Members'
302
                    ),
303
                    'sortClauses' => [new SortClause\ContentId()],
304
                ],
305
                $fixtureDir . 'CustomFieldGt.php',
306
            ],
307
            22 => [
308
                [
309
                    'filter' => new Criterion\CustomField(
310
                        'user_group_name_value_s',
311
                        Criterion\Operator::GTE,
312
                        'Members'
313
                    ),
314
                    'sortClauses' => [new SortClause\ContentId()],
315
                ],
316
                $fixtureDir . 'CustomFieldGte.php',
317
            ],
318
            23 => [
319
                [
320
                    'filter' => new Criterion\CustomField(
321
                        'user_group_name_value_s',
322
                        Criterion\Operator::BETWEEN,
323
                        ['Administrator users', 'Members']
324
                    ),
325
                    'sortClauses' => [new SortClause\ContentId()],
326
                ],
327
                $fixtureDir . 'CustomFieldBetween.php',
328
            ],
329
            24 => [
330
                [
331
                    'filter' => new Criterion\RemoteId(
332
                        ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca']
333
                    ),
334
                    'sortClauses' => [new SortClause\ContentId()],
335
                ],
336
                $fixtureDir . 'RemoteId.php',
337
            ],
338
            25 => [
339
                [
340
                    'filter' => new Criterion\SectionId(
341
                        [2]
342
                    ),
343
                    'sortClauses' => [new SortClause\ContentId()],
344
                ],
345
                $fixtureDir . 'SectionId.php',
346
            ],
347
            26 => [
348
                [
349
                    'filter' => new Criterion\Field(
350
                        'name',
351
                        Criterion\Operator::EQ,
352
                        'Members'
353
                    ),
354
                    'sortClauses' => [new SortClause\ContentId()],
355
                ],
356
                $fixtureDir . 'Field.php',
357
            ],
358
            27 => [
359
                [
360
                    'filter' => new Criterion\Field(
361
                        'name',
362
                        Criterion\Operator::IN,
363
                        ['Members', 'Anonymous Users']
364
                    ),
365
                    'sortClauses' => [new SortClause\ContentId()],
366
                ],
367
                $fixtureDir . 'FieldIn.php',
368
            ],
369
            28 => [
370
                [
371
                    'filter' => new Criterion\DateMetadata(
372
                        Criterion\DateMetadata::MODIFIED,
373
                        Criterion\Operator::BETWEEN,
374
                        [1033920275, 1033920794]
375
                    ),
376
                    'sortClauses' => [new SortClause\ContentId()],
377
                ],
378
                $fixtureDir . 'FieldBetween.php',
379
            ],
380
            29 => [
381
                [
382
                    'filter' => new Criterion\LogicalOr(
383
                        [
384
                            new Criterion\Field(
385
                                'name',
386
                                Criterion\Operator::EQ,
387
                                'Members'
388
                            ),
389
                            new Criterion\DateMetadata(
390
                                Criterion\DateMetadata::MODIFIED,
391
                                Criterion\Operator::BETWEEN,
392
                                [1033920275, 1033920794]
393
                            ),
394
                        ]
395
                    ),
396
                    'sortClauses' => [new SortClause\ContentId()],
397
                ],
398
                $fixtureDir . 'FieldOr.php',
399
            ],
400
            30 => [
401
                [
402
                    'filter' => new Criterion\Subtree(
403
                        '/1/5/'
404
                    ),
405
                    'sortClauses' => [new SortClause\ContentId()],
406
                ],
407
                $fixtureDir . 'Subtree.php',
408
            ],
409
            31 => [
410
                [
411
                    'filter' => new Criterion\LocationId(
412
                        [1, 2, 5]
413
                    ),
414
                    'sortClauses' => [new SortClause\ContentId()],
415
                ],
416
                $fixtureDir . 'LocationId.php',
417
            ],
418
            32 => [
419
                [
420
                    'filter' => new Criterion\ParentLocationId(
421
                        [1]
422
                    ),
423
                    'sortClauses' => [new SortClause\ContentId()],
424
                ],
425
                $fixtureDir . 'ParentLocationId.php',
426
            ],
427
            33 => [
428
                [
429
                    'filter' => new Criterion\LocationRemoteId(
430
                        ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983']
431
                    ),
432
                    'sortClauses' => [new SortClause\ContentId()],
433
                ],
434
                $fixtureDir . 'LocationRemoteId.php',
435
            ],
436
            34 => [
437
                [
438
                    // There is no Status Criterion anymore, this should match all published as well
439
                    'filter' => new Criterion\Subtree(
440
                        '/1/'
441
                    ),
442
                    'sortClauses' => [new SortClause\ContentId()],
443
                    'limit' => 50,
444
                ],
445
                $fixtureDir . 'Status.php',
446
                // Result having the same sort level should be sorted between them to be system independent
447
                function (&$data) {
448
                    usort(
449
                        $data->searchHits,
450
                        function ($a, $b) {
451
                            if ($a->score == $b->score) {
452
                                if ($a->valueObject['id'] == $b->valueObject['id']) {
453
                                    return 0;
454
                                }
455
456
                                // Order by ascending ID
457
                                return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
458
                            }
459
460
                            // Order by descending score
461
                            return ($a->score > $b->score) ? -1 : 1;
462
                        }
463
                    );
464
                },
465
            ],
466
            35 => [
467
                [
468
                    'filter' => new Criterion\UserMetadata(
469
                        Criterion\UserMetadata::MODIFIER,
470
                        Criterion\Operator::EQ,
471
                        14
472
                    ),
473
                    'sortClauses' => [
474
                        new SortClause\ContentId(),
475
                    ],
476
                    'limit' => 50,
477
                ],
478
                $fixtureDir . 'UserMetadata.php',
479
            ],
480
            36 => [
481
                [
482
                    'filter' => new Criterion\UserMetadata(
483
                        Criterion\UserMetadata::MODIFIER,
484
                        Criterion\Operator::IN,
485
                        [14]
486
                    ),
487
                    'sortClauses' => [
488
                        new SortClause\ContentId(),
489
                    ],
490
                    'limit' => 50,
491
                ],
492
                $fixtureDir . 'UserMetadata.php',
493
            ],
494
            37 => [
495
                [
496
                    'filter' => new Criterion\UserMetadata(
497
                        Criterion\UserMetadata::OWNER,
498
                        Criterion\Operator::EQ,
499
                        14
500
                    ),
501
                    'sortClauses' => [
502
                        new SortClause\ContentId(),
503
                    ],
504
                    'limit' => 50,
505
                ],
506
                $fixtureDir . 'UserMetadata.php',
507
            ],
508
            38 => [
509
                [
510
                    'filter' => new Criterion\UserMetadata(
511
                        Criterion\UserMetadata::OWNER,
512
                        Criterion\Operator::IN,
513
                        [14]
514
                    ),
515
                    'sortClauses' => [
516
                        new SortClause\ContentId(),
517
                    ],
518
                    'limit' => 50,
519
                ],
520
                $fixtureDir . 'UserMetadata.php',
521
            ],
522
            39 => [
523
                [
524
                    'filter' => new Criterion\UserMetadata(
525
                        Criterion\UserMetadata::GROUP,
526
                        Criterion\Operator::EQ,
527
                        12
528
                    ),
529
                    'sortClauses' => [
530
                        new SortClause\ContentId(),
531
                    ],
532
                    'limit' => 50,
533
                ],
534
                $fixtureDir . 'UserMetadata.php',
535
            ],
536
            40 => [
537
                [
538
                    'filter' => new Criterion\UserMetadata(
539
                        Criterion\UserMetadata::GROUP,
540
                        Criterion\Operator::IN,
541
                        [12]
542
                    ),
543
                    'sortClauses' => [
544
                        new SortClause\ContentId(),
545
                    ],
546
                    'limit' => 50,
547
                ],
548
                $fixtureDir . 'UserMetadata.php',
549
            ],
550
            41 => [
551
                [
552
                    'filter' => new Criterion\UserMetadata(
553
                        Criterion\UserMetadata::GROUP,
554
                        Criterion\Operator::EQ,
555
                        4
556
                    ),
557
                    'sortClauses' => [
558
                        new SortClause\ContentId(),
559
                    ],
560
                    'limit' => 50,
561
                ],
562
                $fixtureDir . 'UserMetadata.php',
563
            ],
564
            42 => [
565
                [
566
                    'filter' => new Criterion\UserMetadata(
567
                        Criterion\UserMetadata::GROUP,
568
                        Criterion\Operator::IN,
569
                        [4]
570
                    ),
571
                    'sortClauses' => [
572
                        new SortClause\ContentId(),
573
                    ],
574
                    'limit' => 50,
575
                ],
576
                $fixtureDir . 'UserMetadata.php',
577
            ],
578
            43 => [
579
                [
580
                    'filter' => new Criterion\Ancestor(
581
                        [
582
                            '/1/5/44/',
583
                            '/1/5/44/45/',
584
                        ]
585
                    ),
586
                    'sortClauses' => [
587
                        new SortClause\ContentId(),
588
                    ],
589
                    'limit' => 50,
590
                ],
591
                $fixtureDir . 'AncestorContent.php',
592
            ],
593
        ];
594
    }
595
596
    public function getContentQuerySearches()
597
    {
598
        $fixtureDir = $this->getFixtureDir();
599
600
        return [
601
            [
602
                [
603
                    'filter' => new Criterion\ContentId(
604
                        [58, 10]
605
                    ),
606
                    'query' => new Criterion\FullText('contact'),
607
                    'sortClauses' => [new SortClause\ContentId()],
608
                ],
609
                $fixtureDir . 'FullTextFiltered.php',
610
            ],
611
            [
612
                [
613
                    'query' => new Criterion\FullText(
614
                        'contact',
615
                        [
616
                            'boost' => [
617
                                'title' => 2,
618
                            ],
619
                            'fuzziness' => .5,
620
                        ]
621
                    ),
622
                    'sortClauses' => [new SortClause\ContentId()],
623
                ],
624
                $fixtureDir . 'FullText.php',
625
            ],
626
            [
627
                [
628
                    'query' => new Criterion\FullText(
629
                        'Contact*'
630
                    ),
631
                    'sortClauses' => [new SortClause\ContentId()],
632
                ],
633
                $fixtureDir . 'FullTextWildcard.php',
634
            ],
635
            [
636
                [
637
                    'query' => new Criterion\LanguageCode('eng-GB', false),
638
                    'sortClauses' => [new SortClause\ContentId()],
639
                ],
640
                $fixtureDir . 'LanguageCode.php',
641
            ],
642
            [
643
                [
644
                    'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']),
645
                    'offset' => 10,
646
                    'sortClauses' => [new SortClause\ContentId()],
647
                ],
648
                $fixtureDir . 'LanguageCodeIn.php',
649
            ],
650
            [
651
                [
652
                    'query' => new Criterion\LanguageCode('eng-GB'),
653
                    'offset' => 10,
654
                    'sortClauses' => [new SortClause\ContentId()],
655
                ],
656
                $fixtureDir . 'LanguageCodeAlwaysAvailable.php',
657
            ],
658
            [
659
                [
660
                    'query' => new Criterion\Visibility(
661
                        Criterion\Visibility::VISIBLE
662
                    ),
663
                    'sortClauses' => [new SortClause\ContentId()],
664
                    'limit' => 50,
665
                ],
666
                $fixtureDir . 'Visibility.php',
667
            ],
668
        ];
669
    }
670
671
    public function getLocationQuerySearches()
672
    {
673
        $fixtureDir = $this->getFixtureDir();
674
675
        return [
676
            [
677
                [
678
                    'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1),
679
                    'sortClauses' => [new SortClause\ContentId()],
680
                ],
681
                $fixtureDir . 'Depth.php',
682
            ],
683
            [
684
                [
685
                    'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]),
686
                    'sortClauses' => [new SortClause\ContentId()],
687
                ],
688
                $fixtureDir . 'DepthIn.php',
689
            ],
690
            [
691
                [
692
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2),
693
                    'sortClauses' => [new SortClause\ContentId()],
694
                ],
695
                $fixtureDir . 'DepthGt.php',
696
            ],
697
            [
698
                [
699
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2),
700
                    'sortClauses' => [new SortClause\ContentId()],
701
                    'limit' => 50,
702
                ],
703
                $fixtureDir . 'DepthGte.php',
704
            ],
705
            [
706
                [
707
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2),
708
                    'sortClauses' => [new SortClause\ContentId()],
709
                ],
710
                $fixtureDir . 'Depth.php',
711
            ],
712
            [
713
                [
714
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2),
715
                    'sortClauses' => [new SortClause\ContentId()],
716
                    'limit' => 50,
717
                ],
718
                $fixtureDir . 'DepthLte.php',
719
            ],
720
            [
721
                [
722
                    'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]),
723
                    'sortClauses' => [new SortClause\ContentId()],
724
                    'limit' => 50,
725
                ],
726
                $fixtureDir . 'DepthLte.php',
727
            ],
728
            [
729
                [
730
                    'filter' => new Criterion\Ancestor('/1/5/44/45/'),
731
                    'sortClauses' => [
732
                        new SortClause\Location\Depth(),
733
                    ],
734
                    'limit' => 50,
735
                ],
736
                $fixtureDir . 'AncestorLocation.php',
737
            ],
738
        ];
739
    }
740
741
    /**
742
     * Test for the findContent() method.
743
     *
744
     * @dataProvider getFilterContentSearches
745
     *
746
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
747
     */
748
    public function testFindContentFiltered($queryData, $fixture, $closure = null)
749
    {
750
        $query = new Query($queryData);
751
        $this->assertQueryFixture($query, $fixture, $closure);
752
    }
753
754
    /**
755
     * Test for the findContentInfo() method.
756
     *
757
     * @dataProvider getFilterContentSearches
758
     *
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
     *
819
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
820
     */
821
    public function testQueryContentInfo($queryData, $fixture, $closure = null)
822
    {
823
        $query = new Query($queryData);
824
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
825
    }
826
827
    /**
828
     * Test for the findLocations() method.
829
     *
830
     * @dataProvider getContentQuerySearches
831
     *
832
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
833
     */
834
    public function testQueryContentLocations($queryData, $fixture, $closure = null)
835
    {
836
        $query = new LocationQuery($queryData);
837
        $this->assertQueryFixture($query, $fixture, $closure);
838
    }
839
840
    /**
841
     * Test for the findLocations() method.
842
     *
843
     * @dataProvider getLocationQuerySearches
844
     *
845
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
846
     */
847
    public function testQueryLocations($queryData, $fixture, $closure = null)
848
    {
849
        $query = new LocationQuery($queryData);
850
        $this->assertQueryFixture($query, $fixture, $closure);
851
    }
852
853
    public function getCaseInsensitiveSearches()
854
    {
855
        return [
856
            [
857
                [
858
                    'filter' => new Criterion\Field(
859
                        'name',
860
                        Criterion\Operator::EQ,
861
                        'Members'
862
                    ),
863
                    'sortClauses' => [new SortClause\ContentId()],
864
                ],
865
            ],
866
            [
867
                [
868
                    'filter' => new Criterion\Field(
869
                        'name',
870
                        Criterion\Operator::EQ,
871
                        'members'
872
                    ),
873
                    'sortClauses' => [new SortClause\ContentId()],
874
                ],
875
            ],
876
            [
877
                [
878
                    'filter' => new Criterion\Field(
879
                        'name',
880
                        Criterion\Operator::EQ,
881
                        'MEMBERS'
882
                    ),
883
                    'sortClauses' => [new SortClause\ContentId()],
884
                ],
885
            ],
886
        ];
887
    }
888
889
    /**
890
     * Test for the findContent() method.
891
     *
892
     * @dataProvider getCaseInsensitiveSearches
893
     *
894
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
895
     */
896
    public function testFindContentFieldFiltersCaseSensitivity($queryData)
897
    {
898
        $query = new Query($queryData);
899
        $this->assertQueryFixture(
900
            $query,
901
            $this->getFixtureDir() . 'Field.php'
902
        );
903
    }
904
905
    /**
906
     * Test for the findLocations() method.
907
     *
908
     * @dataProvider getCaseInsensitiveSearches
909
     *
910
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
911
     */
912
    public function testFindLocationsFieldFiltersCaseSensitivity($queryData)
913
    {
914
        $query = new LocationQuery($queryData);
915
        $this->assertQueryFixture(
916
            $query,
917
            $this->getFixtureDir() . 'Field.php'
918
        );
919
    }
920
921
    public function getRelationFieldFilterSearches()
922
    {
923
        $fixtureDir = $this->getFixtureDir();
924
925
        return [
926
            0 => [
927
                [
928
                    'filter' => new Criterion\FieldRelation(
929
                        'image',
930
                        Criterion\Operator::IN,
931
                        [1, 4, 10]
932
                    ),
933
                    'sortClauses' => [new SortClause\ContentId()],
934
                ],
935
                $fixtureDir . 'FieldRelation.php',
936
            ],
937
            1 => [
938
                [
939
                    'filter' => new Criterion\FieldRelation(
940
                        'image',
941
                        Criterion\Operator::IN,
942
                        [4, 49]
943
                    ),
944
                    'sortClauses' => [new SortClause\ContentId()],
945
                ],
946
                $fixtureDir . 'FieldRelationAll.php',
947
            ],
948
            2 => [
949
                [
950
                    'filter' => new Criterion\FieldRelation(
951
                        'image',
952
                        Criterion\Operator::IN,
953
                        [4]
954
                    ),
955
                    'sortClauses' => [new SortClause\ContentId()],
956
                ],
957
                $fixtureDir . 'FieldRelation.php',
958
            ],
959
            3 => [
960
                [
961
                    'filter' => new Criterion\FieldRelation(
962
                        'image',
963
                        Criterion\Operator::CONTAINS,
964
                        [1, 4, 10]
965
                    ),
966
                    'sortClauses' => [new SortClause\ContentId()],
967
                ],
968
                $fixtureDir . 'MatchNone.php',
969
            ],
970
            4 => [
971
                [
972
                    'filter' => new Criterion\FieldRelation(
973
                        'image',
974
                        Criterion\Operator::CONTAINS,
975
                        [4, 49]
976
                    ),
977
                    'sortClauses' => [new SortClause\ContentId()],
978
                ],
979
                $fixtureDir . 'MatchNone.php',
980
            ],
981
            5 => [
982
                [
983
                    'filter' => new Criterion\FieldRelation(
984
                        'image',
985
                        Criterion\Operator::CONTAINS,
986
                        [4]
987
                    ),
988
                    'sortClauses' => [new SortClause\ContentId()],
989
                ],
990
                $fixtureDir . 'FieldRelation.php',
991
            ],
992
        ];
993
    }
994
995
    /**
996
     * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered()
997
     * and testFindRelationFieldLocationsFiltered().
998
     */
999
    public function testRelationContentCreation()
1000
    {
1001
        $repository = $this->getRepository();
1002
        $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery');
1003
        $contentService = $repository->getContentService();
1004
        $locationService = $repository->getLocationService();
1005
1006
        $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home
1007
1008
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
1009
        $createStruct->setField('name', 'Image gallery');
1010
        $createStruct->setField('image', 49); // Images folder
1011
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1012
        $contentService->publishVersion($draft->getVersionInfo());
1013
1014
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
1015
        $createStruct->setField('name', 'User gallery');
1016
        $createStruct->setField('image', 4); // User folder
1017
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1018
        $contentService->publishVersion($draft->getVersionInfo());
1019
1020
        $this->refreshSearch($repository);
1021
    }
1022
1023
    /**
1024
     * Test for FieldRelation using findContentInfo() method.
1025
     *
1026
     * @dataProvider getRelationFieldFilterSearches
1027
     *
1028
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
1029
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1030
     */
1031
    public function testFindRelationFieldContentInfoFiltered($queryData, $fixture)
1032
    {
1033
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1034
        $query = new Query($queryData);
1035
        $this->assertQueryFixture($query, $fixture, null, true, true, false);
1036
    }
1037
1038
    /**
1039
     * Test for FieldRelation using findLocations() method.
1040
     *
1041
     * @dataProvider getRelationFieldFilterSearches
1042
     *
1043
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
1044
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1045
     */
1046
    public function testFindRelationFieldLocationsFiltered($queryData, $fixture)
1047
    {
1048
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1049
        $query = new LocationQuery($queryData);
1050
        $this->assertQueryFixture($query, $fixture, null, true, false, false);
1051
    }
1052
1053
    public function testFindSingle()
1054
    {
1055
        $repository = $this->getRepository();
1056
        $searchService = $repository->getSearchService();
1057
1058
        $content = $searchService->findSingle(
1059
            new Criterion\ContentId(
1060
                [4]
1061
            )
1062
        );
1063
1064
        $this->assertEquals(
1065
            4,
1066
            $content->id
1067
        );
1068
    }
1069
1070
    public function testFindNoPerformCount()
1071
    {
1072
        $repository = $this->getRepository();
1073
        $searchService = $repository->getSearchService();
1074
1075
        $query = new Query();
1076
        $query->performCount = false;
1077
        $query->query = new Criterion\ContentTypeId(
1078
            [4]
1079
        );
1080
1081
        $searchHit = $searchService->findContent($query);
1082
1083
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1084
            $this->assertNull(
1085
                $searchHit->totalCount
1086
            );
1087
        } else {
1088
            $this->assertEquals(
1089
                2,
1090
                $searchHit->totalCount
1091
            );
1092
        }
1093
    }
1094
1095
    /**
1096
     * @expectedException \RuntimeException
1097
     */
1098
    public function testFindNoPerformCountException()
1099
    {
1100
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1101
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1102
        }
1103
1104
        $repository = $this->getRepository();
1105
        $searchService = $repository->getSearchService();
1106
1107
        $query = new Query();
1108
        $query->performCount = false;
1109
        $query->limit = 0;
1110
        $query->query = new Criterion\ContentTypeId(
1111
            [4]
1112
        );
1113
1114
        $searchService->findContent($query);
1115
    }
1116
1117
    public function testFindLocationsNoPerformCount()
1118
    {
1119
        $repository = $this->getRepository();
1120
        $searchService = $repository->getSearchService();
1121
1122
        $query = new LocationQuery();
1123
        $query->performCount = false;
1124
        $query->query = new Criterion\ContentTypeId(
1125
            [4]
1126
        );
1127
1128
        $searchHit = $searchService->findLocations($query);
1129
1130
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1131
            $this->assertNull(
1132
                $searchHit->totalCount
1133
            );
1134
        } else {
1135
            $this->assertEquals(
1136
                2,
1137
                $searchHit->totalCount
1138
            );
1139
        }
1140
    }
1141
1142
    /**
1143
     * @expectedException \RuntimeException
1144
     */
1145
    public function testFindLocationsNoPerformCountException()
1146
    {
1147
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1148
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1149
        }
1150
1151
        $repository = $this->getRepository();
1152
        $searchService = $repository->getSearchService();
1153
1154
        $query = new LocationQuery();
1155
        $query->performCount = false;
1156
        $query->limit = 0;
1157
        $query->query = new Criterion\ContentTypeId(
1158
            [4]
1159
        );
1160
1161
        $searchService->findLocations($query);
1162
    }
1163
1164
    /**
1165
     * Create movie Content with subtitle field set to null.
1166
     *
1167
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1168
     */
1169
    protected function createMovieContent()
1170
    {
1171
        $movies = [];
1172
1173
        $repository = $this->getRepository();
1174
        $contentTypeService = $repository->getContentTypeService();
1175
        $contentService = $repository->getContentService();
1176
1177
        $createStruct = $contentTypeService->newContentTypeCreateStruct('movie');
1178
        $createStruct->mainLanguageCode = 'eng-GB';
1179
        $createStruct->remoteId = 'movie-123';
1180
        $createStruct->names = ['eng-GB' => 'Movie'];
1181
        $createStruct->creatorId = 14;
1182
        $createStruct->creationDate = new \DateTime();
1183
1184
        $fieldTitle = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring');
1185
        $fieldTitle->names = ['eng-GB' => 'Title'];
1186
        $fieldTitle->fieldGroup = 'main';
1187
        $fieldTitle->position = 1;
1188
        $fieldTitle->isTranslatable = false;
1189
        $fieldTitle->isSearchable = true;
1190
        $fieldTitle->isRequired = true;
1191
        $createStruct->addFieldDefinition($fieldTitle);
1192
1193
        $fieldSubtitle = $contentTypeService->newFieldDefinitionCreateStruct('subtitle', 'ezstring');
1194
        $fieldSubtitle->names = ['eng-GB' => 'Subtitle'];
1195
        $fieldSubtitle->fieldGroup = 'main';
1196
        $fieldSubtitle->position = 2;
1197
        $fieldSubtitle->isTranslatable = false;
1198
        $fieldSubtitle->isSearchable = true;
1199
        $fieldSubtitle->isRequired = false;
1200
        $createStruct->addFieldDefinition($fieldSubtitle);
1201
1202
        $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1203
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentTypeGroup]);
1204
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1205
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1206
1207
        $createStructRambo = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1208
        $createStructRambo->remoteId = 'movie-456';
1209
        $createStructRambo->alwaysAvailable = false;
1210
        $createStructRambo->setField('title', 'Rambo');
1211
1212
        $ramboDraft = $contentService->createContent($createStructRambo);
1213
        $movies[] = $contentService->publishVersion($ramboDraft->getVersionInfo());
1214
        $this->refreshSearch($repository);
1215
        $createStructRobocop = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1216
        $createStructRobocop->remoteId = 'movie-789';
1217
        $createStructRobocop->alwaysAvailable = false;
1218
        $createStructRobocop->setField('title', 'Robocop');
1219
        $createStructRobocop->setField('subtitle', '');
1220
1221
        $robocopDraft = $contentService->createContent($createStructRobocop);
1222
        $movies[] = $contentService->publishVersion($robocopDraft->getVersionInfo());
1223
        $this->refreshSearch($repository);
1224
        $createStructLastHope = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1225
        $createStructLastHope->remoteId = 'movie-101112';
1226
        $createStructLastHope->alwaysAvailable = false;
1227
        $createStructLastHope->setField('title', 'Star Wars');
1228
        $createStructLastHope->setField('subtitle', 'Last Hope');
1229
1230
        $lastHopeDraft = $contentService->createContent($createStructLastHope);
1231
        $movies[] = $contentService->publishVersion($lastHopeDraft->getVersionInfo());
1232
1233
        $this->refreshSearch($repository);
1234
1235
        return $movies;
1236
    }
1237
1238
    /**
1239
     * Create test Content with ezcountry field having multiple countries selected.
1240
     *
1241
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1242
     */
1243
    protected function createMultipleCountriesContent()
1244
    {
1245
        $repository = $this->getRepository();
1246
        $contentTypeService = $repository->getContentTypeService();
1247
        $contentService = $repository->getContentService();
1248
1249
        $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple');
1250
        $createStruct->mainLanguageCode = 'eng-GB';
1251
        $createStruct->remoteId = 'countries-multiple-123';
1252
        $createStruct->names = ['eng-GB' => 'Multiple countries'];
1253
        $createStruct->creatorId = 14;
1254
        $createStruct->creationDate = new \DateTime();
1255
1256
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry');
1257
        $fieldCreate->names = ['eng-GB' => 'Countries'];
1258
        $fieldCreate->fieldGroup = 'main';
1259
        $fieldCreate->position = 1;
1260
        $fieldCreate->isTranslatable = false;
1261
        $fieldCreate->isSearchable = true;
1262
        $fieldCreate->fieldSettings = ['isMultiple' => true];
1263
1264
        $createStruct->addFieldDefinition($fieldCreate);
1265
1266
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1267
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1268
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1269
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1270
1271
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1272
        $createStruct->remoteId = 'countries-multiple-456';
1273
        $createStruct->alwaysAvailable = false;
1274
        $createStruct->setField(
1275
            'countries',
1276
            ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU']
1277
        );
1278
1279
        $draft = $contentService->createContent($createStruct);
1280
        $content = $contentService->publishVersion($draft->getVersionInfo());
1281
1282
        $this->refreshSearch($repository);
1283
1284
        return $content;
1285
    }
1286
1287
    /**
1288
     * Test for the findContent() method.
1289
     *
1290
     * @return \eZ\Publish\API\Repository\Values\Content\Content[]
1291
     *
1292
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1293
     */
1294
    public function testFieldIsEmpty()
1295
    {
1296
        $testContents = $this->createMovieContent();
1297
1298
        $query = new Query(
1299
            [
1300
                'query' => new Criterion\IsFieldEmpty('subtitle'),
1301
            ]
1302
        );
1303
1304
        $repository = $this->getRepository();
1305
        $searchService = $repository->getSearchService();
1306
        $result = $searchService->findContent($query, ['eng-GB']);
1307
1308
        $this->assertEquals(2, $result->totalCount);
1309
1310
        $this->assertEquals(
1311
            $testContents[0]->id,
1312
            $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...
1313
        );
1314
        $this->assertEquals(
1315
            $testContents[1]->id,
1316
            $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...
1317
        );
1318
1319
        return $testContents;
1320
    }
1321
1322
    /**
1323
     * Test for the findContent() method.
1324
     *
1325
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1326
     */
1327
    public function testFieldIsNotEmpty()
1328
    {
1329
        $testContents = $this->createMovieContent();
1330
1331
        $query = new Query(
1332
            [
1333
                'query' => new Criterion\IsFieldEmpty(
1334
                    'subtitle',
1335
                    false
1336
                ),
1337
            ]
1338
        );
1339
1340
        $repository = $this->getRepository();
1341
        $searchService = $repository->getSearchService();
1342
        $result = $searchService->findContent($query, ['eng-GB']);
1343
1344
        $this->assertEquals(1, $result->totalCount);
1345
        $this->assertEquals(
1346
            $testContents[2]->id,
1347
            $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...
1348
        );
1349
    }
1350
1351
    /**
1352
     * Test for the findContent() method.
1353
     *
1354
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1355
     */
1356
    public function testFieldCollectionContains()
1357
    {
1358
        $testContent = $this->createMultipleCountriesContent();
1359
1360
        $query = new Query(
1361
            [
1362
                'query' => new Criterion\Field(
1363
                    'countries',
1364
                    Criterion\Operator::CONTAINS,
1365
                    'Belgium'
1366
                ),
1367
            ]
1368
        );
1369
1370
        $repository = $this->getRepository();
1371
        $searchService = $repository->getSearchService();
1372
        $result = $searchService->findContent($query);
1373
1374
        $this->assertEquals(1, $result->totalCount);
1375
        $this->assertEquals(
1376
            $testContent->id,
1377
            $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...
1378
        );
1379
    }
1380
1381
    /**
1382
     * Test for the findContent() method.
1383
     *
1384
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1385
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains
1386
     */
1387
    public function testFieldCollectionContainsNoMatch()
1388
    {
1389
        $this->createMultipleCountriesContent();
1390
        $query = new Query(
1391
            [
1392
                'query' => new Criterion\Field(
1393
                    'countries',
1394
                    Criterion\Operator::CONTAINS,
1395
                    'Netherlands Antilles'
1396
                ),
1397
            ]
1398
        );
1399
1400
        $repository = $this->getRepository();
1401
        $searchService = $repository->getSearchService();
1402
        $result = $searchService->findContent($query);
1403
1404
        $this->assertEquals(0, $result->totalCount);
1405
    }
1406
1407
    /**
1408
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1409
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1410
     */
1411
    public function testInvalidFieldIdentifierRange()
1412
    {
1413
        $repository = $this->getRepository();
1414
        $searchService = $repository->getSearchService();
1415
1416
        $searchService->findContent(
1417
            new Query(
1418
                [
1419
                    'filter' => new Criterion\Field(
1420
                        'some_hopefully_unknown_field',
1421
                        Criterion\Operator::BETWEEN,
1422
                        [10, 1000]
1423
                    ),
1424
                    'sortClauses' => [new SortClause\ContentId()],
1425
                ]
1426
            )
1427
        );
1428
    }
1429
1430
    /**
1431
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1432
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'some_hopefully_unknown_field'
1433
     */
1434
    public function testInvalidFieldIdentifierIn()
1435
    {
1436
        $repository = $this->getRepository();
1437
        $searchService = $repository->getSearchService();
1438
1439
        $searchService->findContent(
1440
            new Query(
1441
                [
1442
                    'filter' => new Criterion\Field(
1443
                        'some_hopefully_unknown_field',
1444
                        Criterion\Operator::EQ,
1445
                        1000
1446
                    ),
1447
                    'sortClauses' => [new SortClause\ContentId()],
1448
                ]
1449
            )
1450
        );
1451
    }
1452
1453
    /**
1454
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1455
     * @expectedExceptionMessage Argument '$criterion->target' is invalid: No searchable fields found for the given criterion target 'tag_cloud_url'
1456
     */
1457
    public function testFindContentWithNonSearchableField()
1458
    {
1459
        $repository = $this->getRepository();
1460
        $searchService = $repository->getSearchService();
1461
1462
        $searchService->findContent(
1463
            new Query(
1464
                [
1465
                    'filter' => new Criterion\Field(
1466
                        'tag_cloud_url',
1467
                        Criterion\Operator::EQ,
1468
                        'http://nimbus.com'
1469
                    ),
1470
                    'sortClauses' => [new SortClause\ContentId()],
1471
                ]
1472
            )
1473
        );
1474
    }
1475
1476
    /**
1477
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1478
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1479
     */
1480
    public function testSortFieldWithNonSearchableField()
1481
    {
1482
        $repository = $this->getRepository();
1483
        $searchService = $repository->getSearchService();
1484
1485
        $searchService->findContent(
1486
            new Query(
1487
                [
1488
                    'sortClauses' => [new SortClause\Field('template_look', 'title')],
1489
                ]
1490
            )
1491
        );
1492
    }
1493
1494
    /**
1495
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1496
     * @expectedExceptionMessage Argument '$sortClause->targetData' is invalid: No searchable fields found for the given sort clause target 'title' on 'template_look'
1497
     */
1498
    public function testSortMapLocationDistanceWithNonSearchableField()
1499
    {
1500
        $repository = $this->getRepository();
1501
        $searchService = $repository->getSearchService();
1502
1503
        $searchService->findContent(
1504
            new Query(
1505
                [
1506
                    'sortClauses' => [
1507
                        new SortClause\MapLocationDistance(
1508
                            'template_look',
1509
                            'title',
1510
                            1,
1511
                            2
1512
                        ),
1513
                    ],
1514
                ]
1515
            )
1516
        );
1517
    }
1518
1519
    /**
1520
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1521
     */
1522
    public function testFindSingleFailMultiple()
1523
    {
1524
        $repository = $this->getRepository();
1525
        $searchService = $repository->getSearchService();
1526
1527
        $searchService->findSingle(
1528
            new Criterion\ContentId(
1529
                [4, 10]
1530
            )
1531
        );
1532
    }
1533
1534
    /**
1535
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
1536
     */
1537
    public function testFindSingleWithNonSearchableField()
1538
    {
1539
        $repository = $this->getRepository();
1540
        $searchService = $repository->getSearchService();
1541
1542
        $searchService->findSingle(
1543
            new Criterion\Field(
1544
                'tag_cloud_url',
1545
                Criterion\Operator::EQ,
1546
                'http://nimbus.com'
1547
            )
1548
        );
1549
    }
1550
1551
    public function getSortedContentSearches()
1552
    {
1553
        $fixtureDir = $this->getFixtureDir();
1554
1555
        return [
1556
            0 => [
1557
                [
1558
                    'filter' => new Criterion\SectionId([2]),
1559
                    'offset' => 0,
1560
                    'limit' => 10,
1561
                    'sortClauses' => [],
1562
                ],
1563
                $fixtureDir . 'SortNone.php',
1564
                // Result having the same sort level should be sorted between them to be system independent
1565
                function (&$data) {
1566
                    usort(
1567
                        $data->searchHits,
1568
                        function ($a, $b) {
1569
                            return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
1570
                        }
1571
                    );
1572
                },
1573
            ],
1574
            1 => [
1575
                [
1576
                    'filter' => new Criterion\SectionId([2]),
1577
                    'offset' => 0,
1578
                    'limit' => 10,
1579
                    'sortClauses' => [
1580
                        new SortClause\DatePublished(),
1581
                        new SortClause\ContentId(),
1582
                    ],
1583
                ],
1584
                $fixtureDir . 'SortDatePublished.php',
1585
            ],
1586
            2 => [
1587
                [
1588
                    'filter' => new Criterion\SectionId([2]),
1589
                    'offset' => 0,
1590
                    'limit' => 50,
1591
                    'sortClauses' => [
1592
                        new SortClause\DateModified(),
1593
                        new SortClause\ContentId(),
1594
                    ],
1595
                ],
1596
                $fixtureDir . 'SortDateModified.php',
1597
            ],
1598
            3 => [
1599
                [
1600
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1601
                    'offset' => 0,
1602
                    'limit' => 50,
1603
                    'sortClauses' => [
1604
                        new SortClause\SectionIdentifier(),
1605
                        new SortClause\ContentId(),
1606
                    ],
1607
                ],
1608
                $fixtureDir . 'SortSectionIdentifier.php',
1609
            ],
1610
            4 => [
1611
                [
1612
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1613
                    'offset' => 0,
1614
                    'limit' => 50,
1615
                    'sortClauses' => [
1616
                        new SortClause\SectionName(),
1617
                        new SortClause\ContentId(),
1618
                    ],
1619
                ],
1620
                $fixtureDir . 'SortSectionName.php',
1621
            ],
1622
            5 => [
1623
                [
1624
                    'filter' => new Criterion\SectionId([2, 3]),
1625
                    'offset' => 0,
1626
                    'limit' => 50,
1627
                    'sortClauses' => [
1628
                        new SortClause\ContentName(),
1629
                        new SortClause\ContentId(),
1630
                    ],
1631
                ],
1632
                $fixtureDir . 'SortContentName.php',
1633
            ],
1634
            6 => [
1635
                [
1636
                    'filter' => new Criterion\ContentTypeId(1),
1637
                    'offset' => 0,
1638
                    'limit' => 50,
1639
                    'sortClauses' => [
1640
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1641
                        new SortClause\ContentId(),
1642
                    ],
1643
                ],
1644
                $fixtureDir . 'SortFolderName.php',
1645
            ],
1646
            7 => [
1647
                [
1648
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1649
                    'offset' => 0,
1650
                    'limit' => 50,
1651
                    'sortClauses' => [
1652
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1653
                        new SortClause\ContentId(),
1654
                    ],
1655
                ],
1656
                $fixtureDir . 'SortFieldMultipleTypes.php',
1657
            ],
1658
            8 => [
1659
                [
1660
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1661
                    'offset' => 0,
1662
                    'limit' => 50,
1663
                    'sortClauses' => [
1664
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1665
                        new SortClause\ContentId(),
1666
                    ],
1667
                ],
1668
                $fixtureDir . 'SortFieldMultipleTypesReverse.php',
1669
            ],
1670
            9 => [
1671
                [
1672
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1673
                    'offset' => 3,
1674
                    'limit' => 5,
1675
                    'sortClauses' => [
1676
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1677
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1678
                        new SortClause\ContentId(),
1679
                    ],
1680
                ],
1681
                $fixtureDir . 'SortFieldMultipleTypesSlice.php',
1682
            ],
1683
            10 => [
1684
                [
1685
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1686
                    'offset' => 3,
1687
                    'limit' => 5,
1688
                    'sortClauses' => [
1689
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1690
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1691
                        new SortClause\ContentId(),
1692
                    ],
1693
                ],
1694
                $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php',
1695
            ],
1696
        ];
1697
    }
1698
1699
    public function getSortedLocationSearches()
1700
    {
1701
        $fixtureDir = $this->getFixtureDir();
1702
1703
        return [
1704
            [
1705
                [
1706
                    'filter' => new Criterion\SectionId([2]),
1707
                    'offset' => 0,
1708
                    'limit' => 10,
1709
                    'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)],
1710
                ],
1711
                $fixtureDir . 'SortPathString.php',
1712
            ],
1713
            [
1714
                [
1715
                    'filter' => new Criterion\SectionId([2]),
1716
                    'offset' => 0,
1717
                    'limit' => 10,
1718
                    'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)],
1719
                ],
1720
                $fixtureDir . 'SortLocationDepth.php',
1721
                // Result having the same sort level should be sorted between them to be system independent
1722
                function (&$data) {
1723
                    // Result with ids:
1724
                    //     4 has depth = 1
1725
                    //     11, 12, 13, 42, 59 have depth = 2
1726
                    //     10, 14 have depth = 3
1727
                    $map = [
1728
                        4 => 0,
1729
                        11 => 1,
1730
                        12 => 2,
1731
                        13 => 3,
1732
                        42 => 4,
1733
                        59 => 5,
1734
                        10 => 6,
1735
                        14 => 7,
1736
                    ];
1737
                    usort(
1738
                        $data->searchHits,
1739
                        function ($a, $b) use ($map) {
1740
                            return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1;
1741
                        }
1742
                    );
1743
                },
1744
            ],
1745
            [
1746
                [
1747
                    'filter' => new Criterion\SectionId([3]),
1748
                    'offset' => 0,
1749
                    'limit' => 10,
1750
                    'sortClauses' => [
1751
                        new SortClause\Location\Path(Query::SORT_DESC),
1752
                        new SortClause\ContentName(Query::SORT_ASC),
1753
                    ],
1754
                ],
1755
                $fixtureDir . 'SortMultiple.php',
1756
            ],
1757
            [
1758
                [
1759
                    'filter' => new Criterion\SectionId([2]),
1760
                    'offset' => 0,
1761
                    'limit' => 10,
1762
                    'sortClauses' => [
1763
                        new SortClause\Location\Priority(Query::SORT_DESC),
1764
                        new SortClause\ContentId(),
1765
                    ],
1766
                ],
1767
                $fixtureDir . 'SortDesc.php',
1768
            ],
1769
        ];
1770
    }
1771
1772
    /**
1773
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
1774
     */
1775
    protected function createTestContentType()
1776
    {
1777
        $repository = $this->getRepository();
1778
        $contentTypeService = $repository->getContentTypeService();
1779
1780
        $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type');
1781
        $createStruct->mainLanguageCode = 'eng-GB';
1782
        $createStruct->names = ['eng-GB' => 'Test type'];
1783
        $createStruct->creatorId = 14;
1784
        $createStruct->creationDate = new \DateTime();
1785
1786
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger');
1787
        $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field'];
1788
        $translatableFieldCreate->fieldGroup = 'main';
1789
        $translatableFieldCreate->position = 1;
1790
        $translatableFieldCreate->isTranslatable = true;
1791
        $translatableFieldCreate->isSearchable = true;
1792
1793
        $createStruct->addFieldDefinition($translatableFieldCreate);
1794
1795
        $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger');
1796
        $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field'];
1797
        $nonTranslatableFieldCreate->fieldGroup = 'main';
1798
        $nonTranslatableFieldCreate->position = 2;
1799
        $nonTranslatableFieldCreate->isTranslatable = false;
1800
        $nonTranslatableFieldCreate->isSearchable = true;
1801
1802
        $createStruct->addFieldDefinition($nonTranslatableFieldCreate);
1803
1804
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1805
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1806
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1807
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1808
1809
        return $contentType;
1810
    }
1811
1812
    /**
1813
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1814
     * @param int $fieldValue11 Value for translatable field in first language
1815
     * @param int $fieldValue12 Value for translatable field in second language
1816
     * @param int $fieldValue2 Value for non translatable field
1817
     * @param string $mainLanguageCode
1818
     * @param bool $alwaysAvailable
1819
     *
1820
     * @return Content
1821
     */
1822
    protected function createMultilingualContent(
1823
        $contentType,
1824
        $fieldValue11 = null,
1825
        $fieldValue12 = null,
1826
        $fieldValue2 = null,
1827
        $mainLanguageCode = 'eng-GB',
1828
        $alwaysAvailable = false
1829
    ) {
1830
        $repository = $this->getRepository();
1831
        $contentService = $repository->getContentService();
1832
1833
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1834
        $createStruct->alwaysAvailable = $alwaysAvailable;
1835
        $createStruct->mainLanguageCode = $mainLanguageCode;
1836
        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...
1837
            $createStruct->setField('integer', $fieldValue11, 'eng-GB');
1838
        }
1839
        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...
1840
            $createStruct->setField('integer', $fieldValue12, 'ger-DE');
1841
        }
1842
        $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode);
1843
1844
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
1845
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1846
        $content = $contentService->publishVersion($draft->getVersionInfo());
1847
1848
        $this->refreshSearch($repository);
1849
1850
        return $content;
1851
    }
1852
1853
    protected function checkPrioritizedLanguagesSupport()
1854
    {
1855
        $setupFactory = $this->getSetupFactory();
1856
        if ($setupFactory instanceof LegacyElasticsearch) {
1857
            $this->markTestIncomplete('Prioritized languages are not supported with Elasticsearch engine');
1858
        }
1859
    }
1860
1861
    public function providerForTestMultilingualFieldSort()
1862
    {
1863
        return [
1864
            0 => [
1865
                [
1866
                    1 => [1, 2, 1],
1867
                    2 => [2, 1, 2],
1868
                    3 => [2, 1, 3],
1869
                    4 => [1, 2, 4],
1870
                ],
1871
                [
1872
                    'languages' => [
1873
                        'eng-GB',
1874
                        'ger-DE',
1875
                    ],
1876
                ],
1877
                [
1878
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1879
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1880
                ],
1881
                /**
1882
                 * Expected order, Value eng-GB, Value ger-DE.
1883
                 *
1884
                 * Content 4, 1, 2, 4
1885
                 * Content 1, 1, 2, 1
1886
                 * Content 3, 2, 1, 3
1887
                 * Content 2, 2, 1, 2
1888
                 */
1889
                [4, 1, 3, 2],
1890
            ],
1891
            1 => [
1892
                [
1893
                    1 => [1, 2, 1],
1894
                    2 => [2, 1, 2],
1895
                    3 => [2, 1, 3],
1896
                    4 => [1, 2, 4],
1897
                ],
1898
                [
1899
                    'languages' => [
1900
                        'ger-DE',
1901
                        'eng-GB',
1902
                    ],
1903
                ],
1904
                [
1905
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1906
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1907
                ],
1908
                /**
1909
                 * Expected order, Value eng-GB, Value ger-DE.
1910
                 *
1911
                 * Content 3, 2, 1, 3
1912
                 * Content 2, 2, 1, 2
1913
                 * Content 4, 1, 2, 4
1914
                 * Content 1, 1, 2, 1
1915
                 */
1916
                [3, 2, 4, 1],
1917
            ],
1918
            2 => [
1919
                [
1920
                    1 => [null, 2, null, 'ger-DE'],
1921
                    2 => [3, null, null, 'eng-GB'],
1922
                    3 => [4, null, null, 'eng-GB'],
1923
                    4 => [null, 1, null, 'ger-DE'],
1924
                ],
1925
                [
1926
                    'languages' => [
1927
                        'eng-GB',
1928
                    ],
1929
                ],
1930
                [
1931
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1932
                ],
1933
                /**
1934
                 * Expected order, Value eng-GB, Value ger-DE.
1935
                 *
1936
                 * Content 3, 4, -
1937
                 * Content 2, 3, -
1938
                 */
1939
                [3, 2],
1940
            ],
1941
            3 => [
1942
                [
1943
                    1 => [null, 2, null, 'ger-DE'],
1944
                    2 => [3, null, null, 'eng-GB'],
1945
                    3 => [4, null, null, 'eng-GB'],
1946
                    4 => [null, 1, null, 'ger-DE'],
1947
                ],
1948
                [
1949
                    'languages' => [
1950
                        'ger-DE',
1951
                    ],
1952
                ],
1953
                [
1954
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1955
                ],
1956
                /**
1957
                 * Expected order, Value eng-GB, Value ger-DE.
1958
                 *
1959
                 * Content 1, -, 2
1960
                 * Content 4, -, 1
1961
                 */
1962
                [1, 4],
1963
            ],
1964
            4 => [
1965
                [
1966
                    1 => [null, 2, null, 'ger-DE'],
1967
                    2 => [3, null, null, 'eng-GB'],
1968
                    3 => [4, null, null, 'eng-GB'],
1969
                    4 => [null, 1, null, 'ger-DE'],
1970
                ],
1971
                [
1972
                    'languages' => [
1973
                        'eng-GB',
1974
                        'ger-DE',
1975
                    ],
1976
                ],
1977
                [
1978
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1979
                ],
1980
                /**
1981
                 * Expected order, Value eng-GB, Value ger-DE.
1982
                 *
1983
                 * Content 3, 4, -
1984
                 * Content 2, 3, -
1985
                 * Content 1, -, 2
1986
                 * Content 4, -, 1
1987
                 */
1988
                [3, 2, 1, 4],
1989
            ],
1990
            5 => [
1991
                [
1992
                    1 => [null, 2, null, 'ger-DE'],
1993
                    2 => [3, null, null, 'eng-GB'],
1994
                    3 => [4, null, null, 'eng-GB'],
1995
                    4 => [null, 1, null, 'ger-DE'],
1996
                ],
1997
                [
1998
                    'languages' => [
1999
                        'ger-DE',
2000
                        'eng-GB',
2001
                    ],
2002
                ],
2003
                [
2004
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2005
                ],
2006
                /**
2007
                 * Expected order, Value eng-GB, Value ger-DE.
2008
                 *
2009
                 * Content 3, 4, -
2010
                 * Content 2, 3, -
2011
                 * Content 1, -, 2
2012
                 * Content 4, -, 1
2013
                 */
2014
                [3, 2, 1, 4],
2015
            ],
2016
            6 => [
2017
                [
2018
                    1 => [null, 2, null, 'ger-DE'],
2019
                    2 => [3, 4, null, 'eng-GB'],
2020
                    3 => [4, 3, null, 'eng-GB'],
2021
                    4 => [null, 1, null, 'ger-DE'],
2022
                ],
2023
                [
2024
                    'languages' => [
2025
                        'eng-GB',
2026
                        'ger-DE',
2027
                    ],
2028
                ],
2029
                [
2030
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2031
                ],
2032
                /**
2033
                 * Expected order, Value eng-GB, Value ger-DE.
2034
                 *
2035
                 * Content 3, 4, 3
2036
                 * Content 2, 3, 4
2037
                 * Content 1, -, 2
2038
                 * Content 4, -, 1
2039
                 */
2040
                [3, 2, 1, 4],
2041
            ],
2042
            7 => [
2043
                [
2044
                    1 => [null, 2, null, 'ger-DE'],
2045
                    2 => [3, 4, null, 'eng-GB'],
2046
                    3 => [4, 3, null, 'eng-GB'],
2047
                    4 => [null, 1, null, 'ger-DE'],
2048
                ],
2049
                [
2050
                    'languages' => [
2051
                        'ger-DE',
2052
                        'eng-GB',
2053
                    ],
2054
                ],
2055
                [
2056
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2057
                ],
2058
                /**
2059
                 * Expected order, Value eng-GB, Value ger-DE.
2060
                 *
2061
                 * Content 2, 3, 4
2062
                 * Content 3, 4, 3
2063
                 * Content 1, -, 2
2064
                 * Content 4, -, 1
2065
                 */
2066
                [2, 3, 1, 4],
2067
            ],
2068
            8 => [
2069
                [
2070
                    1 => [null, 1, null, 'ger-DE', true],
2071
                    2 => [4, null, null, 'eng-GB', true],
2072
                    3 => [3, null, null, 'eng-GB', false],
2073
                    4 => [null, 2, null, 'ger-DE', false],
2074
                ],
2075
                [
2076
                    'languages' => [
2077
                        'eng-GB',
2078
                    ],
2079
                ],
2080
                [
2081
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
2082
                ],
2083
                /**
2084
                 * Expected order, Value eng-GB, Value ger-DE.
2085
                 *
2086
                 * Content 1, -, 1
2087
                 * Content 3, 3, -
2088
                 * Content 2, 4, -
2089
                 */
2090
                [1, 3, 2],
2091
            ],
2092
            9 => [
2093
                [
2094
                    1 => [null, 1, null, 'ger-DE', true],
2095
                    2 => [4, null, null, 'eng-GB', true],
2096
                    3 => [3, null, null, 'eng-GB', false],
2097
                    4 => [null, 2, null, 'ger-DE', false],
2098
                ],
2099
                [
2100
                    'languages' => [
2101
                        'ger-DE',
2102
                    ],
2103
                ],
2104
                [
2105
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2106
                ],
2107
                /**
2108
                 * Expected order, Value eng-GB, Value ger-DE.
2109
                 *
2110
                 * Content 2, 4, -
2111
                 * Content 4, -, 2
2112
                 * Content 1, -, 1
2113
                 */
2114
                [2, 4, 1],
2115
            ],
2116
            10 => [
2117
                [
2118
                    1 => [null, 1, null, 'ger-DE', true],
2119
                    2 => [4, null, null, 'eng-GB', true],
2120
                    3 => [3, null, null, 'eng-GB', false],
2121
                    4 => [null, 2, null, 'ger-DE', false],
2122
                ],
2123
                [
2124
                    'languages' => [
2125
                        'eng-GB',
2126
                    ],
2127
                    'useAlwaysAvailable' => false,
2128
                ],
2129
                [
2130
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
2131
                ],
2132
                /**
2133
                 * Expected order, Value eng-GB, Value ger-DE.
2134
                 *
2135
                 * Content 3, 3, -
2136
                 * Content 2, 4, -
2137
                 */
2138
                [3, 2],
2139
            ],
2140
            11 => [
2141
                [
2142
                    1 => [null, 1, null, 'ger-DE', true],
2143
                    2 => [4, null, null, 'eng-GB', true],
2144
                    3 => [3, null, null, 'eng-GB', false],
2145
                    4 => [null, 2, null, 'ger-DE', false],
2146
                ],
2147
                [
2148
                    'languages' => [
2149
                        'ger-DE',
2150
                    ],
2151
                    'useAlwaysAvailable' => false,
2152
                ],
2153
                [
2154
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
2155
                ],
2156
                /**
2157
                 * Expected order, Value eng-GB, Value ger-DE.
2158
                 *
2159
                 * Content 4, -, 2
2160
                 * Content 1, -, 1
2161
                 */
2162
                [4, 1],
2163
            ],
2164
        ];
2165
    }
2166
2167
    /**
2168
     * Test for the findContent() method.
2169
     *
2170
     * @group rrr
2171
     * @dataProvider providerForTestMultilingualFieldSort
2172
     *
2173
     * @param array $contentDataList
2174
     * @param array $languageSettings
2175
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2176
     * @param array $expected
2177
     */
2178
    public function testMultilingualFieldSortContent(
2179
        array $contentDataList,
2180
        $languageSettings,
2181
        array $sortClauses,
2182
        $expected
2183
    ) {
2184
        $this->assertMultilingualFieldSort(
2185
            $contentDataList,
2186
            $languageSettings,
2187
            $sortClauses,
2188
            $expected
2189
        );
2190
    }
2191
2192
    /**
2193
     * Test for the findLocations() method.
2194
     *
2195
     * @group rrr
2196
     * @dataProvider providerForTestMultilingualFieldSort
2197
     *
2198
     * @param array $contentDataList
2199
     * @param array $languageSettings
2200
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2201
     * @param array $expected
2202
     */
2203
    public function testMultilingualFieldSortLocation(
2204
        array $contentDataList,
2205
        $languageSettings,
2206
        array $sortClauses,
2207
        $expected
2208
    ) {
2209
        $this->assertMultilingualFieldSort(
2210
            $contentDataList,
2211
            $languageSettings,
2212
            $sortClauses,
2213
            $expected,
2214
            false
2215
        );
2216
    }
2217
2218
    /**
2219
     * @param array $contentDataList
2220
     * @param array $languageSettings
2221
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2222
     * @param array $expected
2223
     * @param bool $contentSearch
2224
     */
2225
    protected function assertMultilingualFieldSort(
2226
        array $contentDataList,
2227
        $languageSettings,
2228
        array $sortClauses,
2229
        $expected,
2230
        $contentSearch = true
2231
    ) {
2232
        $this->checkPrioritizedLanguagesSupport();
2233
        $contentType = $this->createTestContentType();
2234
2235
        // Create a draft to account for behaviour with ContentType in different states
2236
        $repository = $this->getRepository();
2237
        $contentTypeService = $repository->getContentTypeService();
2238
        $contentTypeService->createContentTypeDraft($contentType);
2239
2240
        $defaults = [null, null, null, 'eng-GB', false];
2241
        $contentIdList = [];
2242
        foreach ($contentDataList as $key => $contentData) {
2243
            $contentData = $contentData + $defaults;
2244
            list(
2245
                $fieldValue11,
2246
                $fieldValue12,
2247
                $fieldValue2,
2248
                $mainLanguageCode,
2249
                $alwaysAvailable
2250
            ) = $contentData;
2251
2252
            $contentIdList[$key] = $this->createMultilingualContent(
2253
                $contentType,
2254
                $fieldValue11,
2255
                $fieldValue12,
2256
                $fieldValue2,
2257
                $mainLanguageCode,
2258
                $alwaysAvailable
2259
            )->id;
2260
        }
2261
2262
        // "article" type Content is not matched, this ensures that non-matched
2263
        // field does not affect sort
2264
        $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC);
2265
        array_unshift($sortClauses, $dummySortClause);
2266
        $sortClauses[] = $dummySortClause;
2267
2268
        $searchService = $repository->getSearchService();
2269
        if ($contentSearch) {
2270
            $query = new Query(
2271
                [
2272
                    'query' => new Criterion\ContentTypeId($contentType->id),
2273
                    'sortClauses' => $sortClauses,
2274
                ]
2275
            );
2276
            $result = $searchService->findContent($query, $languageSettings);
2277
        } else {
2278
            $query = new LocationQuery(
2279
                [
2280
                    'query' => new Criterion\ContentTypeId($contentType->id),
2281
                    'sortClauses' => $sortClauses,
2282
                ]
2283
            );
2284
            $result = $searchService->findLocations($query, $languageSettings);
2285
        }
2286
2287
        $this->assertEquals(count($expected), $result->totalCount);
2288
2289
        $expectedIdList = [];
2290
        foreach ($expected as $contentNumber) {
2291
            $expectedIdList[] = $contentIdList[$contentNumber];
2292
        }
2293
2294
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2295
    }
2296
2297
    public function providerForTestMultilingualFieldFilter()
2298
    {
2299
        return [
2300
            0 => [
2301
                $fixture = [
2302
                    1 => [null, 1, null, 'ger-DE', true],
2303
                    2 => [4, null, null, 'eng-GB', true],
2304
                    3 => [3, null, null, 'eng-GB', false],
2305
                    4 => [null, 2, null, 'ger-DE', false],
2306
                    5 => [5, null, null, 'eng-GB', true],
2307
                ],
2308
                $languageSettings = [
2309
                    'languages' => [
2310
                        'ger-DE',
2311
                    ],
2312
                ],
2313
                new Criterion\Field('integer', Criterion\Operator::LT, 5),
2314
                /**
2315
                 * Expected order, Value eng-GB, Value ger-DE.
2316
                 *
2317
                 * Content 2, 4, -
2318
                 * Content 4, -, 2
2319
                 * Content 1, -, 1
2320
                 */
2321
                [2, 4, 1],
2322
            ],
2323
            1 => [
2324
                $fixture,
2325
                [
2326
                    'languages' => [
2327
                        'ger-DE',
2328
                    ],
2329
                    'useAlwaysAvailable' => false,
2330
                ],
2331
                new Criterion\Field('integer', Criterion\Operator::LT, 2),
2332
                /**
2333
                 * Expected order, Value eng-GB, Value ger-DE.
2334
                 *
2335
                 * Content 1, -, 1
2336
                 */
2337
                [1],
2338
            ],
2339
            2 => [
2340
                $fixture,
2341
                [
2342
                    'languages' => [
2343
                        'eng-GB',
2344
                    ],
2345
                ],
2346
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2347
                /**
2348
                 * Expected order, Value eng-GB, Value ger-DE.
2349
                 *
2350
                 * Content 5, 5, -
2351
                 * Content 2, 4, -
2352
                 * Content 3, 3, -
2353
                 * Content 1, -, 1
2354
                 */
2355
                [2, 3, 1],
2356
            ],
2357
            3 => [
2358
                $fixture,
2359
                [
2360
                    'languages' => [
2361
                        'eng-GB',
2362
                    ],
2363
                    'useAlwaysAvailable' => false,
2364
                ],
2365
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2366
                /**
2367
                 * Expected order, Value eng-GB, Value ger-DE.
2368
                 *
2369
                 * Content 2, 4, -
2370
                 * Content 3, 3, -
2371
                 */
2372
                [2, 3],
2373
            ],
2374
            4 => [
2375
                $fixture,
2376
                $languageSettings,
2377
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2378
                /**
2379
                 * Expected order, Value eng-GB, Value ger-DE.
2380
                 *
2381
                 * Content 2, 4, -
2382
                 * Content 4, -, 2
2383
                 * Content 1, -, 1
2384
                 */
2385
                [2, 4, 1],
2386
            ],
2387
            5 => [
2388
                $fixture,
2389
                $languageSettings,
2390
                new Criterion\Field('integer', Criterion\Operator::GT, 1),
2391
                /**
2392
                 * Expected order, Value eng-GB, Value ger-DE.
2393
                 *
2394
                 * Content 5, 5, -
2395
                 * Content 2, 4, -
2396
                 * Content 4, -, 2
2397
                 */
2398
                [5, 2, 4],
2399
            ],
2400
            6 => [
2401
                $fixture,
2402
                $languageSettings,
2403
                new Criterion\Field('integer', Criterion\Operator::GTE, 2),
2404
                /**
2405
                 * Expected order, Value eng-GB, Value ger-DE.
2406
                 *
2407
                 * Content 5, 5, -
2408
                 * Content 2, 4, -
2409
                 * Content 4, -, 2
2410
                 */
2411
                [5, 2, 4],
2412
            ],
2413
            7 => [
2414
                $fixture,
2415
                $languageSettings,
2416
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]),
2417
                /**
2418
                 * Expected order, Value eng-GB, Value ger-DE.
2419
                 *
2420
                 * Content 2, 4, -
2421
                 * Content 4, -, 2
2422
                 */
2423
                [2, 4],
2424
            ],
2425
            8 => [
2426
                $fixture,
2427
                $languageSettings,
2428
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]),
2429
                [],
2430
            ],
2431
            9 => [
2432
                $fixture,
2433
                $languageSettings,
2434
                new Criterion\Field('integer', Criterion\Operator::EQ, 4),
2435
                /**
2436
                 * Expected order, Value eng-GB, Value ger-DE.
2437
                 *
2438
                 * Content 4, -, 2
2439
                 */
2440
                [2],
2441
            ],
2442
            10 => [
2443
                $fixture,
2444
                $languageSettings,
2445
                new Criterion\Field('integer', Criterion\Operator::EQ, 2),
2446
                /**
2447
                 * Expected order, Value eng-GB, Value ger-DE.
2448
                 *
2449
                 * Content 2, 4, -
2450
                 */
2451
                [4],
2452
            ],
2453
        ];
2454
    }
2455
2456
    /**
2457
     * Test for the findContent() method.
2458
     *
2459
     * @group ttt
2460
     * @dataProvider providerForTestMultilingualFieldFilter
2461
     *
2462
     * @param array $contentDataList
2463
     * @param array $languageSettings
2464
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2465
     * @param array $expected
2466
     */
2467
    public function testMultilingualFieldFilterContent(
2468
        array $contentDataList,
2469
        $languageSettings,
2470
        Criterion $criterion,
2471
        $expected
2472
    ) {
2473
        $this->assertMultilingualFieldFilter(
2474
            $contentDataList,
2475
            $languageSettings,
2476
            $criterion,
2477
            $expected
2478
        );
2479
    }
2480
2481
    /**
2482
     * Test for the findLocations() method.
2483
     *
2484
     * @group ttt
2485
     * @dataProvider providerForTestMultilingualFieldFilter
2486
     *
2487
     * @param array $contentDataList
2488
     * @param array $languageSettings
2489
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2490
     * @param array $expected
2491
     */
2492
    public function testMultilingualFieldFilterLocation(
2493
        array $contentDataList,
2494
        $languageSettings,
2495
        Criterion $criterion,
2496
        $expected
2497
    ) {
2498
        $this->assertMultilingualFieldFilter(
2499
            $contentDataList,
2500
            $languageSettings,
2501
            $criterion,
2502
            $expected,
2503
            false
2504
        );
2505
    }
2506
2507
    /**
2508
     * @param array $contentDataList
2509
     * @param array $languageSettings
2510
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2511
     * @param array $expected
2512
     * @param bool $contentSearch
2513
     */
2514
    protected function assertMultilingualFieldFilter(
2515
        array $contentDataList,
2516
        $languageSettings,
2517
        Criterion $criterion,
2518
        $expected,
2519
        $contentSearch = true
2520
    ) {
2521
        $this->checkPrioritizedLanguagesSupport();
2522
        $contentType = $this->createTestContentType();
2523
2524
        // Create a draft to account for behaviour with ContentType in different states
2525
        $repository = $this->getRepository();
2526
        $contentTypeService = $repository->getContentTypeService();
2527
        $contentTypeService->createContentTypeDraft($contentType);
2528
2529
        $defaults = [null, null, null, 'eng-GB', false];
2530
        $contentIdList = [];
2531
        foreach ($contentDataList as $key => $contentData) {
2532
            $contentData = $contentData + $defaults;
2533
            list(
2534
                $fieldValue11,
2535
                $fieldValue12,
2536
                $fieldValue2,
2537
                $mainLanguageCode,
2538
                $alwaysAvailable
2539
            ) = $contentData;
2540
2541
            $contentIdList[$key] = $this->createMultilingualContent(
2542
                $contentType,
2543
                $fieldValue11,
2544
                $fieldValue12,
2545
                $fieldValue2,
2546
                $mainLanguageCode,
2547
                $alwaysAvailable
2548
            )->id;
2549
        }
2550
2551
        $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC);
2552
        $searchService = $repository->getSearchService();
2553
        if ($contentSearch) {
2554
            $query = new Query(
2555
                [
2556
                    'query' => new Criterion\LogicalAnd(
2557
                        [
2558
                            new Criterion\ContentTypeId($contentType->id),
2559
                            $criterion,
2560
                        ]
2561
                    ),
2562
                    'sortClauses' => [$sortClause],
2563
                ]
2564
            );
2565
            $result = $searchService->findContent($query, $languageSettings);
2566
        } else {
2567
            $query = new LocationQuery(
2568
                [
2569
                    'query' => new Criterion\LogicalAnd(
2570
                        [
2571
                            new Criterion\ContentTypeId($contentType->id),
2572
                            $criterion,
2573
                        ]
2574
                    ),
2575
                    'sortClauses' => [$sortClause],
2576
                ]
2577
            );
2578
            $result = $searchService->findLocations($query, $languageSettings);
2579
        }
2580
2581
        $this->assertEquals(count($expected), $result->totalCount);
2582
2583
        $expectedIdList = [];
2584
        foreach ($expected as $contentNumber) {
2585
            $expectedIdList[] = $contentIdList[$contentNumber];
2586
        }
2587
2588
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2589
    }
2590
2591
    /**
2592
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result
2593
     *
2594
     * @return array
2595
     */
2596
    protected function mapResultContentIds(SearchResult $result)
2597
    {
2598
        return array_map(
2599
            function (SearchHit $searchHit) {
2600
                if ($searchHit->valueObject instanceof Location) {
2601
                    return $searchHit->valueObject->contentInfo->id;
2602
                }
2603
2604
                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...
2605
            },
2606
            $result->searchHits
2607
        );
2608
    }
2609
2610
    /**
2611
     * Test for the findContent() method.
2612
     *
2613
     * @dataProvider getSortedContentSearches
2614
     *
2615
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2616
     */
2617
    public function testFindAndSortContent($queryData, $fixture, $closure = null)
2618
    {
2619
        $query = new Query($queryData);
2620
        $this->assertQueryFixture($query, $fixture, $closure);
2621
    }
2622
2623
    /**
2624
     * Test for the findContentInfo() method.
2625
     *
2626
     * @dataProvider getSortedContentSearches
2627
     *
2628
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2629
     */
2630
    public function testFindAndSortContentInfo($queryData, $fixture, $closure = null)
2631
    {
2632
        $query = new Query($queryData);
2633
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
2634
    }
2635
2636
    /**
2637
     * Test for the findLocations() method.
2638
     *
2639
     * @dataProvider getSortedContentSearches
2640
     *
2641
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2642
     */
2643
    public function testFindAndSortContentLocations($queryData, $fixture, $closure = null)
2644
    {
2645
        $query = new LocationQuery($queryData);
2646
        $this->assertQueryFixture($query, $fixture, $closure);
2647
    }
2648
2649
    /**
2650
     * Test for the findLocations() method.
2651
     *
2652
     * @dataProvider getSortedLocationSearches
2653
     *
2654
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2655
     */
2656
    public function testFindAndSortLocations($queryData, $fixture, $closure = null)
2657
    {
2658
        $query = new LocationQuery($queryData);
2659
        $this->assertQueryFixture($query, $fixture, $closure);
2660
    }
2661
2662
    /**
2663
     * Test for the findContent() method.
2664
     *
2665
     * @dataProvider getFacetedSearches
2666
     *
2667
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2668
     */
2669
    public function testFindFacetedContent(Query $query, $fixture)
2670
    {
2671
        $this->assertQueryFixture($query, $fixture);
2672
    }
2673
2674
    /**
2675
     * Test for the findContentInfo() method.
2676
     *
2677
     * @dataProvider getFacetedSearches
2678
     *
2679
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2680
     */
2681
    public function testFindFacetedContentInfo(Query $query, $fixture)
2682
    {
2683
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true);
2684
    }
2685
2686
    /**
2687
     * Test for the findContent() method.
2688
     *
2689
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2690
     */
2691
    public function testQueryCustomField()
2692
    {
2693
        $query = new Query(
2694
            [
2695
                'query' => new Criterion\CustomField(
2696
                    'custom_field',
2697
                    Criterion\Operator::EQ,
2698
                    'AdMiNiStRaToR'
2699
                ),
2700
                'offset' => 0,
2701
                'limit' => 10,
2702
                'sortClauses' => [new SortClause\ContentId()],
2703
            ]
2704
        );
2705
        $this->assertQueryFixture(
2706
            $query,
2707
            $this->getFixtureDir() . '/QueryCustomField.php'
2708
        );
2709
    }
2710
2711
    /**
2712
     * Test for the findContent() method.
2713
     *
2714
     * This tests explicitely queries the first_name while user is contained in
2715
     * the last_name of admin and anonymous. This is done to show the custom
2716
     * copy field working.
2717
     *
2718
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2719
     */
2720
    public function testQueryModifiedField()
2721
    {
2722
        // Check using get_class since the others extend SetupFactory\Legacy
2723
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2724
            $this->markTestIncomplete(
2725
                'Custom fields not supported by LegacySE ' .
2726
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2727
            );
2728
        }
2729
2730
        $query = new Query(
2731
            [
2732
                'query' => new Criterion\Field(
2733
                    'first_name',
2734
                    Criterion\Operator::EQ,
2735
                    'User'
2736
                ),
2737
                'offset' => 0,
2738
                'limit' => 10,
2739
                'sortClauses' => [new SortClause\ContentId()],
2740
            ]
2741
        );
2742
        $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...
2743
2744
        $this->assertQueryFixture(
2745
            $query,
2746
            $this->getFixtureDir() . '/QueryModifiedField.php'
2747
        );
2748
    }
2749
2750
    /**
2751
     * Test for the findContent() method.
2752
     *
2753
     * This tests first explicitly creates sort clause on the 'short_name' which is empty
2754
     * for all Content instances of 'folder' ContentType. Custom sort field is then set
2755
     * to the index storage name of folder's 'name' field, in order to show the custom
2756
     * sort field working.
2757
     *
2758
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2759
     */
2760
    public function testSortModifiedField()
2761
    {
2762
        // Check using get_class since the others extend SetupFactory\Legacy
2763
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2764
            $this->markTestIncomplete(
2765
                'Custom field sort not supported by LegacySE ' .
2766
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2767
            );
2768
        }
2769
2770
        $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC);
2771
        $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s');
2772
2773
        $query = new Query(
2774
            [
2775
                'filter' => new Criterion\ContentTypeId(1),
2776
                'offset' => 0,
2777
                'limit' => 10,
2778
                'sortClauses' => [
2779
                    $sortClause,
2780
                    new SortClause\ContentId(),
2781
                ],
2782
            ]
2783
        );
2784
2785
        $this->assertQueryFixture(
2786
            $query,
2787
            $this->getFixtureDir() . '/SortFolderName.php'
2788
        );
2789
    }
2790
2791
    /**
2792
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
2793
     */
2794
    protected function createTestPlaceContentType()
2795
    {
2796
        $repository = $this->getRepository();
2797
        $contentTypeService = $repository->getContentTypeService();
2798
2799
        $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype');
2800
        $createStruct->mainLanguageCode = 'eng-GB';
2801
        $createStruct->names = ['eng-GB' => 'Test type'];
2802
        $createStruct->creatorId = 14;
2803
        $createStruct->creationDate = new \DateTime();
2804
2805
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation');
2806
        $translatableFieldCreate->names = ['eng-GB' => 'Map location field'];
2807
        $translatableFieldCreate->fieldGroup = 'main';
2808
        $translatableFieldCreate->position = 1;
2809
        $translatableFieldCreate->isTranslatable = false;
2810
        $translatableFieldCreate->isSearchable = true;
2811
2812
        $createStruct->addFieldDefinition($translatableFieldCreate);
2813
2814
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
2815
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
2816
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
2817
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
2818
2819
        return $contentType;
2820
    }
2821
2822
    /**
2823
     * Test for the findContent() method.
2824
     *
2825
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2826
     * @group maplocation
2827
     */
2828
    public function testMapLocationDistanceLessThanOrEqual()
2829
    {
2830
        $contentType = $this->createTestPlaceContentType();
2831
2832
        // Create a draft to account for behaviour with ContentType in different states
2833
        $repository = $this->getRepository();
2834
        $contentTypeService = $repository->getContentTypeService();
2835
        $contentService = $repository->getContentService();
2836
        $contentTypeService->createContentTypeDraft($contentType);
2837
2838
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2839
        $createStruct->alwaysAvailable = false;
2840
        $createStruct->mainLanguageCode = 'eng-GB';
2841
        $createStruct->setField(
2842
            'maplocation',
2843
            [
2844
                'latitude' => 45.894877,
2845
                'longitude' => 15.972699,
2846
                'address' => 'Here be wild boars',
2847
            ],
2848
            'eng-GB'
2849
        );
2850
2851
        $draft = $contentService->createContent($createStruct);
2852
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2853
2854
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2855
        $createStruct->alwaysAvailable = false;
2856
        $createStruct->mainLanguageCode = 'eng-GB';
2857
        $createStruct->setField(
2858
            'maplocation',
2859
            [
2860
                'latitude' => 45.927334,
2861
                'longitude' => 15.934847,
2862
                'address' => 'A lone tree',
2863
            ],
2864
            'eng-GB'
2865
        );
2866
2867
        $draft = $contentService->createContent($createStruct);
2868
        $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...
2869
2870
        $this->refreshSearch($repository);
2871
2872
        $query = new Query(
2873
            [
2874
                'filter' => new Criterion\LogicalAnd(
2875
                    [
2876
                        new Criterion\ContentTypeId($contentType->id),
2877
                        new Criterion\MapLocationDistance(
2878
                            'maplocation',
2879
                            Criterion\Operator::LTE,
2880
                            240,
2881
                            43.756825,
2882
                            15.775074
2883
                        ),
2884
                    ]
2885
                ),
2886
                'offset' => 0,
2887
                'limit' => 10,
2888
                'sortClauses' => [],
2889
            ]
2890
        );
2891
2892
        $searchService = $repository->getSearchService();
2893
        $result = $searchService->findContent($query);
2894
2895
        $this->assertEquals(1, $result->totalCount);
2896
        $this->assertEquals(
2897
            $wildBoars->id,
2898
            $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...
2899
        );
2900
    }
2901
2902
    /**
2903
     * Test for the findContent() method.
2904
     *
2905
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2906
     * @group maplocation
2907
     */
2908
    public function testMapLocationDistanceGreaterThanOrEqual()
2909
    {
2910
        $contentType = $this->createTestPlaceContentType();
2911
2912
        // Create a draft to account for behaviour with ContentType in different states
2913
        $repository = $this->getRepository();
2914
        $contentTypeService = $repository->getContentTypeService();
2915
        $contentService = $repository->getContentService();
2916
        $contentTypeService->createContentTypeDraft($contentType);
2917
2918
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2919
        $createStruct->alwaysAvailable = false;
2920
        $createStruct->mainLanguageCode = 'eng-GB';
2921
        $createStruct->setField(
2922
            'maplocation',
2923
            [
2924
                'latitude' => 45.894877,
2925
                'longitude' => 15.972699,
2926
                'address' => 'Here be wild boars',
2927
            ],
2928
            'eng-GB'
2929
        );
2930
2931
        $draft = $contentService->createContent($createStruct);
2932
        $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...
2933
2934
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2935
        $createStruct->alwaysAvailable = false;
2936
        $createStruct->mainLanguageCode = 'eng-GB';
2937
        $createStruct->setField(
2938
            'maplocation',
2939
            [
2940
                'latitude' => 45.927334,
2941
                'longitude' => 15.934847,
2942
                'address' => 'A lone tree',
2943
            ],
2944
            'eng-GB'
2945
        );
2946
2947
        $draft = $contentService->createContent($createStruct);
2948
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2949
2950
        $this->refreshSearch($repository);
2951
2952
        $query = new Query(
2953
            [
2954
                'filter' => new Criterion\LogicalAnd(
2955
                    [
2956
                        new Criterion\ContentTypeId($contentType->id),
2957
                        new Criterion\MapLocationDistance(
2958
                            'maplocation',
2959
                            Criterion\Operator::GTE,
2960
                            240,
2961
                            43.756825,
2962
                            15.775074
2963
                        ),
2964
                    ]
2965
                ),
2966
                'offset' => 0,
2967
                'limit' => 10,
2968
                'sortClauses' => [],
2969
            ]
2970
        );
2971
2972
        $searchService = $repository->getSearchService();
2973
        $result = $searchService->findContent($query);
2974
2975
        $this->assertEquals(1, $result->totalCount);
2976
        $this->assertEquals(
2977
            $tree->id,
2978
            $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...
2979
        );
2980
    }
2981
2982
    /**
2983
     * Test for the findContent() method.
2984
     *
2985
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2986
     * @group maplocation
2987
     */
2988
    public function testMapLocationDistanceBetween()
2989
    {
2990
        $contentType = $this->createTestPlaceContentType();
2991
2992
        // Create a draft to account for behaviour with ContentType in different states
2993
        $repository = $this->getRepository();
2994
        $contentTypeService = $repository->getContentTypeService();
2995
        $contentService = $repository->getContentService();
2996
        $contentTypeService->createContentTypeDraft($contentType);
2997
2998
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2999
        $createStruct->alwaysAvailable = false;
3000
        $createStruct->mainLanguageCode = 'eng-GB';
3001
        $createStruct->setField(
3002
            'maplocation',
3003
            [
3004
                'latitude' => 45.894877,
3005
                'longitude' => 15.972699,
3006
                'address' => 'Here be wild boars',
3007
            ],
3008
            'eng-GB'
3009
        );
3010
3011
        $draft = $contentService->createContent($createStruct);
3012
        $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...
3013
3014
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3015
        $createStruct->alwaysAvailable = false;
3016
        $createStruct->mainLanguageCode = 'eng-GB';
3017
        $createStruct->setField(
3018
            'maplocation',
3019
            [
3020
                'latitude' => 45.927334,
3021
                'longitude' => 15.934847,
3022
                'address' => 'A lone tree',
3023
            ],
3024
            'eng-GB'
3025
        );
3026
3027
        $draft = $contentService->createContent($createStruct);
3028
        $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...
3029
3030
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3031
        $createStruct->alwaysAvailable = false;
3032
        $createStruct->mainLanguageCode = 'eng-GB';
3033
        $createStruct->setField(
3034
            'maplocation',
3035
            [
3036
                'latitude' => 45.903777,
3037
                'longitude' => 15.958788,
3038
                'address' => 'Meadow with mushrooms',
3039
            ],
3040
            'eng-GB'
3041
        );
3042
3043
        $draft = $contentService->createContent($createStruct);
3044
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3045
3046
        $this->refreshSearch($repository);
3047
3048
        $query = new Query(
3049
            [
3050
                'filter' => new Criterion\LogicalAnd(
3051
                    [
3052
                        new Criterion\ContentTypeId($contentType->id),
3053
                        new Criterion\MapLocationDistance(
3054
                            'maplocation',
3055
                            Criterion\Operator::BETWEEN,
3056
                            [239, 241],
3057
                            43.756825,
3058
                            15.775074
3059
                        ),
3060
                    ]
3061
                ),
3062
                'offset' => 0,
3063
                'limit' => 10,
3064
                'sortClauses' => [],
3065
            ]
3066
        );
3067
3068
        $searchService = $repository->getSearchService();
3069
        $result = $searchService->findContent($query);
3070
3071
        $this->assertEquals(1, $result->totalCount);
3072
        $this->assertEquals(
3073
            $mushrooms->id,
3074
            $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...
3075
        );
3076
    }
3077
3078
    /**
3079
     * Test for the findContent() method.
3080
     *
3081
     * This tests the distance over the pole. The tests intentionally uses large range,
3082
     * as the flat Earth model used in Legacy Storage Search is not precise for the use case.
3083
     * What is tested here is that outer bounding box is correctly calculated, so that
3084
     * location is not excluded.
3085
     *
3086
     * Range between 222km and 350km shows the magnitude of error between great-circle
3087
     * (always very precise) and flat Earth (very imprecise for this use case) models.
3088
     *
3089
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3090
     * @group maplocation
3091
     */
3092
    public function testMapLocationDistanceBetweenPolar()
3093
    {
3094
        $contentType = $this->createTestPlaceContentType();
3095
3096
        // Create a draft to account for behaviour with ContentType in different states
3097
        $repository = $this->getRepository();
3098
        $contentTypeService = $repository->getContentTypeService();
3099
        $contentService = $repository->getContentService();
3100
        $contentTypeService->createContentTypeDraft($contentType);
3101
3102
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3103
        $createStruct->alwaysAvailable = false;
3104
        $createStruct->mainLanguageCode = 'eng-GB';
3105
        $createStruct->setField(
3106
            'maplocation',
3107
            [
3108
                'latitude' => 89,
3109
                'longitude' => -164,
3110
                'address' => 'Polar bear media tower',
3111
            ],
3112
            'eng-GB'
3113
        );
3114
3115
        $draft = $contentService->createContent($createStruct);
3116
        $polarBear = $contentService->publishVersion($draft->getVersionInfo());
3117
3118
        $this->refreshSearch($repository);
3119
3120
        $query = new Query(
3121
            [
3122
                'filter' => new Criterion\LogicalAnd(
3123
                    [
3124
                        new Criterion\ContentTypeId($contentType->id),
3125
                        new Criterion\MapLocationDistance(
3126
                            'maplocation',
3127
                            Criterion\Operator::BETWEEN,
3128
                            [221, 350],
3129
                            89,
3130
                            16
3131
                        ),
3132
                    ]
3133
                ),
3134
                'offset' => 0,
3135
                'limit' => 10,
3136
                'sortClauses' => [],
3137
            ]
3138
        );
3139
3140
        $searchService = $repository->getSearchService();
3141
        $result = $searchService->findContent($query);
3142
3143
        $this->assertEquals(1, $result->totalCount);
3144
        $this->assertEquals(
3145
            $polarBear->id,
3146
            $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...
3147
        );
3148
    }
3149
3150
    /**
3151
     * Test for the findContent() method.
3152
     *
3153
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3154
     * @group maplocation
3155
     */
3156
    public function testMapLocationDistanceSortAscending()
3157
    {
3158
        $contentType = $this->createTestPlaceContentType();
3159
3160
        // Create a draft to account for behaviour with ContentType in different states
3161
        $repository = $this->getRepository();
3162
        $contentTypeService = $repository->getContentTypeService();
3163
        $contentService = $repository->getContentService();
3164
        $contentTypeService->createContentTypeDraft($contentType);
3165
3166
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3167
        $createStruct->alwaysAvailable = false;
3168
        $createStruct->mainLanguageCode = 'eng-GB';
3169
        $createStruct->setField(
3170
            'maplocation',
3171
            [
3172
                'latitude' => 45.894877,
3173
                'longitude' => 15.972699,
3174
                'address' => 'Here be wild boars',
3175
            ],
3176
            'eng-GB'
3177
        );
3178
3179
        $draft = $contentService->createContent($createStruct);
3180
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3181
3182
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3183
        $createStruct->alwaysAvailable = false;
3184
        $createStruct->mainLanguageCode = 'eng-GB';
3185
        $createStruct->setField(
3186
            'maplocation',
3187
            [
3188
                'latitude' => 45.927334,
3189
                'longitude' => 15.934847,
3190
                'address' => 'A lone tree',
3191
            ],
3192
            'eng-GB'
3193
        );
3194
3195
        $draft = $contentService->createContent($createStruct);
3196
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3197
3198
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3199
        $createStruct->alwaysAvailable = false;
3200
        $createStruct->mainLanguageCode = 'eng-GB';
3201
        $createStruct->setField(
3202
            'maplocation',
3203
            [
3204
                'latitude' => 45.903777,
3205
                'longitude' => 15.958788,
3206
                'address' => 'Meadow with mushrooms',
3207
            ],
3208
            'eng-GB'
3209
        );
3210
3211
        $draft = $contentService->createContent($createStruct);
3212
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3213
3214
        $this->refreshSearch($repository);
3215
3216
        $wellInVodice = [
3217
            'latitude' => 43.756825,
3218
            'longitude' => 15.775074,
3219
        ];
3220
3221
        $query = new Query(
3222
            [
3223
                'filter' => new Criterion\LogicalAnd(
3224
                    [
3225
                        new Criterion\ContentTypeId($contentType->id),
3226
                        new Criterion\MapLocationDistance(
3227
                            'maplocation',
3228
                            Criterion\Operator::GTE,
3229
                            235,
3230
                            $wellInVodice['latitude'],
3231
                            $wellInVodice['longitude']
3232
                        ),
3233
                    ]
3234
                ),
3235
                'offset' => 0,
3236
                'limit' => 10,
3237
                'sortClauses' => [
3238
                    new SortClause\MapLocationDistance(
3239
                        'testtype',
3240
                        'maplocation',
3241
                        $wellInVodice['latitude'],
3242
                        $wellInVodice['longitude'],
3243
                        Query::SORT_ASC
3244
                    ),
3245
                ],
3246
            ]
3247
        );
3248
3249
        $searchService = $repository->getSearchService();
3250
        $result = $searchService->findContent($query);
3251
3252
        $this->assertEquals(3, $result->totalCount);
3253
        $this->assertEquals(
3254
            $wildBoars->id,
3255
            $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...
3256
        );
3257
        $this->assertEquals(
3258
            $mushrooms->id,
3259
            $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...
3260
        );
3261
        $this->assertEquals(
3262
            $tree->id,
3263
            $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...
3264
        );
3265
    }
3266
3267
    /**
3268
     * Test for the findContent() method.
3269
     *
3270
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3271
     * @group maplocation
3272
     */
3273
    public function testMapLocationDistanceSortDescending()
3274
    {
3275
        $contentType = $this->createTestPlaceContentType();
3276
3277
        // Create a draft to account for behaviour with ContentType in different states
3278
        $repository = $this->getRepository();
3279
        $contentTypeService = $repository->getContentTypeService();
3280
        $contentService = $repository->getContentService();
3281
        $contentTypeService->createContentTypeDraft($contentType);
3282
3283
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3284
        $createStruct->alwaysAvailable = false;
3285
        $createStruct->mainLanguageCode = 'eng-GB';
3286
        $createStruct->setField(
3287
            'maplocation',
3288
            [
3289
                'latitude' => 45.894877,
3290
                'longitude' => 15.972699,
3291
                'address' => 'Here be wild boars',
3292
            ],
3293
            'eng-GB'
3294
        );
3295
3296
        $draft = $contentService->createContent($createStruct);
3297
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3298
3299
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3300
        $createStruct->alwaysAvailable = false;
3301
        $createStruct->mainLanguageCode = 'eng-GB';
3302
        $createStruct->setField(
3303
            'maplocation',
3304
            [
3305
                'latitude' => 45.927334,
3306
                'longitude' => 15.934847,
3307
                'address' => 'A lone tree',
3308
            ],
3309
            'eng-GB'
3310
        );
3311
3312
        $draft = $contentService->createContent($createStruct);
3313
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3314
3315
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3316
        $createStruct->alwaysAvailable = false;
3317
        $createStruct->mainLanguageCode = 'eng-GB';
3318
        $createStruct->setField(
3319
            'maplocation',
3320
            [
3321
                'latitude' => 45.903777,
3322
                'longitude' => 15.958788,
3323
                'address' => 'Meadow with mushrooms',
3324
            ],
3325
            'eng-GB'
3326
        );
3327
3328
        $draft = $contentService->createContent($createStruct);
3329
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3330
3331
        $this->refreshSearch($repository);
3332
3333
        $well = [
3334
            'latitude' => 43.756825,
3335
            'longitude' => 15.775074,
3336
        ];
3337
3338
        $query = new Query(
3339
            [
3340
                'filter' => new Criterion\LogicalAnd(
3341
                    [
3342
                        new Criterion\ContentTypeId($contentType->id),
3343
                        new Criterion\MapLocationDistance(
3344
                            'maplocation',
3345
                            Criterion\Operator::GTE,
3346
                            235,
3347
                            $well['latitude'],
3348
                            $well['longitude']
3349
                        ),
3350
                    ]
3351
                ),
3352
                'offset' => 0,
3353
                'limit' => 10,
3354
                'sortClauses' => [
3355
                    new SortClause\MapLocationDistance(
3356
                        'testtype',
3357
                        'maplocation',
3358
                        $well['latitude'],
3359
                        $well['longitude'],
3360
                        Query::SORT_DESC
3361
                    ),
3362
                ],
3363
            ]
3364
        );
3365
3366
        $searchService = $repository->getSearchService();
3367
        $result = $searchService->findContent($query);
3368
3369
        $this->assertEquals(3, $result->totalCount);
3370
        $this->assertEquals(
3371
            $wildBoars->id,
3372
            $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...
3373
        );
3374
        $this->assertEquals(
3375
            $mushrooms->id,
3376
            $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...
3377
        );
3378
        $this->assertEquals(
3379
            $tree->id,
3380
            $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...
3381
        );
3382
    }
3383
3384
    /**
3385
     * Test for the findContent() method.
3386
     *
3387
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3388
     * @group maplocation
3389
     */
3390
    public function testMapLocationDistanceWithCustomField()
3391
    {
3392
        $setupFactory = $this->getSetupFactory();
3393
        if ($setupFactory instanceof LegacyElasticsearch) {
3394
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3395
        }
3396
3397
        $contentType = $this->createTestPlaceContentType();
3398
3399
        // Create a draft to account for behaviour with ContentType in different states
3400
        $repository = $this->getRepository();
3401
        $contentTypeService = $repository->getContentTypeService();
3402
        $contentService = $repository->getContentService();
3403
        $contentTypeService->createContentTypeDraft($contentType);
3404
3405
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3406
        $createStruct->alwaysAvailable = false;
3407
        $createStruct->mainLanguageCode = 'eng-GB';
3408
        $createStruct->setField(
3409
            'maplocation',
3410
            [
3411
                'latitude' => 45.894877,
3412
                'longitude' => 15.972699,
3413
                'address' => 'Here be wild boars',
3414
            ],
3415
            'eng-GB'
3416
        );
3417
3418
        $draft = $contentService->createContent($createStruct);
3419
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3420
3421
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3422
        $createStruct->alwaysAvailable = false;
3423
        $createStruct->mainLanguageCode = 'eng-GB';
3424
        $createStruct->setField(
3425
            'maplocation',
3426
            [
3427
                'latitude' => 45.927334,
3428
                'longitude' => 15.934847,
3429
                'address' => 'A lone tree',
3430
            ],
3431
            'eng-GB'
3432
        );
3433
3434
        $draft = $contentService->createContent($createStruct);
3435
        $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...
3436
3437
        $this->refreshSearch($repository);
3438
3439
        $distanceCriterion = new Criterion\MapLocationDistance(
3440
            'maplocation',
3441
            Criterion\Operator::LTE,
3442
            240,
3443
            43.756825,
3444
            15.775074
3445
        );
3446
        $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3447
3448
        $query = new Query(
3449
            [
3450
                'filter' => new Criterion\LogicalAnd(
3451
                    [
3452
                        new Criterion\ContentTypeId($contentType->id),
3453
                        $distanceCriterion,
3454
                    ]
3455
                ),
3456
                'offset' => 0,
3457
                'limit' => 10,
3458
                'sortClauses' => [],
3459
            ]
3460
        );
3461
3462
        $searchService = $repository->getSearchService();
3463
        $result = $searchService->findContent($query);
3464
3465
        $this->assertEquals(1, $result->totalCount);
3466
        $this->assertEquals(
3467
            $wildBoars->id,
3468
            $result->searchHits[0]->valueObject->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<eZ\Publish\API\Re...ory\Values\ValueObject>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
3469
        );
3470
    }
3471
3472
    /**
3473
     * Test for the findContent() method.
3474
     *
3475
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3476
     * @group maplocation
3477
     */
3478
    public function testMapLocationDistanceWithCustomFieldSort()
3479
    {
3480
        $setupFactory = $this->getSetupFactory();
3481
        if ($setupFactory instanceof LegacyElasticsearch) {
3482
            $this->markTestIncomplete("TODO: Some issues with 'copy_to' and 'geo_point'");
3483
        }
3484
3485
        $contentType = $this->createTestPlaceContentType();
3486
3487
        // Create a draft to account for behaviour with ContentType in different states
3488
        $repository = $this->getRepository();
3489
        $contentTypeService = $repository->getContentTypeService();
3490
        $contentService = $repository->getContentService();
3491
        $contentTypeService->createContentTypeDraft($contentType);
3492
3493
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3494
        $createStruct->alwaysAvailable = false;
3495
        $createStruct->mainLanguageCode = 'eng-GB';
3496
        $createStruct->setField(
3497
            'maplocation',
3498
            [
3499
                'latitude' => 45.894877,
3500
                'longitude' => 15.972699,
3501
                'address' => 'Here be wild boars',
3502
            ],
3503
            'eng-GB'
3504
        );
3505
3506
        $draft = $contentService->createContent($createStruct);
3507
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3508
3509
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3510
        $createStruct->alwaysAvailable = false;
3511
        $createStruct->mainLanguageCode = 'eng-GB';
3512
        $createStruct->setField(
3513
            'maplocation',
3514
            [
3515
                'latitude' => 45.927334,
3516
                'longitude' => 15.934847,
3517
                'address' => 'A lone tree',
3518
            ],
3519
            'eng-GB'
3520
        );
3521
3522
        $draft = $contentService->createContent($createStruct);
3523
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3524
3525
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3526
        $createStruct->alwaysAvailable = false;
3527
        $createStruct->mainLanguageCode = 'eng-GB';
3528
        $createStruct->setField(
3529
            'maplocation',
3530
            [
3531
                'latitude' => 45.903777,
3532
                'longitude' => 15.958788,
3533
                'address' => 'Meadow with mushrooms',
3534
            ],
3535
            'eng-GB'
3536
        );
3537
3538
        $draft = $contentService->createContent($createStruct);
3539
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3540
3541
        $this->refreshSearch($repository);
3542
3543
        $well = [
3544
            'latitude' => 43.756825,
3545
            'longitude' => 15.775074,
3546
        ];
3547
3548
        $sortClause = new SortClause\MapLocationDistance(
3549
            'testtype',
3550
            'maplocation',
3551
            $well['latitude'],
3552
            $well['longitude'],
3553
            Query::SORT_DESC
3554
        );
3555
        $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3556
3557
        $query = new Query(
3558
            [
3559
                'filter' => new Criterion\LogicalAnd(
3560
                    [
3561
                        new Criterion\ContentTypeId($contentType->id),
3562
                        new Criterion\MapLocationDistance(
3563
                            'maplocation',
3564
                            Criterion\Operator::GTE,
3565
                            235,
3566
                            $well['latitude'],
3567
                            $well['longitude']
3568
                        ),
3569
                    ]
3570
                ),
3571
                'offset' => 0,
3572
                'limit' => 10,
3573
                'sortClauses' => [
3574
                    $sortClause,
3575
                ],
3576
            ]
3577
        );
3578
3579
        $searchService = $repository->getSearchService();
3580
        $result = $searchService->findContent($query);
3581
3582
        $this->assertEquals(3, $result->totalCount);
3583
        $this->assertEquals(
3584
            $wildBoars->id,
3585
            $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...
3586
        );
3587
        $this->assertEquals(
3588
            $mushrooms->id,
3589
            $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...
3590
        );
3591
        $this->assertEquals(
3592
            $tree->id,
3593
            $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...
3594
        );
3595
    }
3596
3597
    /**
3598
     * Test for the findLocations() method.
3599
     *
3600
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3601
     */
3602
    public function testFindMainLocation()
3603
    {
3604
        $plainSiteLocationId = 56;
3605
        $designLocationId = 58;
3606
        $partnersContentId = 59;
3607
        $repository = $this->getRepository();
3608
        $locationService = $repository->getLocationService();
3609
        $contentService = $repository->getContentService();
3610
3611
        // Add secondary Location for "Partners" user group, under "Design" page
3612
        $locationService->createLocation(
3613
            $contentService->loadContentInfo($partnersContentId),
3614
            $locationService->newLocationCreateStruct($designLocationId)
3615
        );
3616
3617
        $this->refreshSearch($repository);
3618
3619
        $query = new LocationQuery(
3620
            [
3621
                'filter' => new Criterion\LogicalAnd(
3622
                    [
3623
                        new Criterion\ParentLocationId($designLocationId),
3624
                        new Criterion\Location\IsMainLocation(
3625
                            Criterion\Location\IsMainLocation::MAIN
3626
                        ),
3627
                    ]
3628
                ),
3629
                'offset' => 0,
3630
                'limit' => 10,
3631
                'sortClauses' => [],
3632
            ]
3633
        );
3634
3635
        $searchService = $repository->getSearchService();
3636
        $result = $searchService->findLocations($query);
3637
3638
        $this->assertEquals(1, $result->totalCount);
3639
        $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...
3640
    }
3641
3642
    /**
3643
     * Test for the findLocations() method.
3644
     *
3645
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3646
     */
3647
    public function testFindNonMainLocation()
3648
    {
3649
        $designLocationId = 58;
3650
        $partnersContentId = 59;
3651
        $repository = $this->getRepository();
3652
        $locationService = $repository->getLocationService();
3653
        $contentService = $repository->getContentService();
3654
3655
        // Add secondary Location for "Partners" user group, under "Design" page
3656
        $newLocation = $locationService->createLocation(
3657
            $contentService->loadContentInfo($partnersContentId),
3658
            $locationService->newLocationCreateStruct($designLocationId)
3659
        );
3660
3661
        $this->refreshSearch($repository);
3662
3663
        $query = new LocationQuery(
3664
            [
3665
                'filter' => new Criterion\LogicalAnd(
3666
                    [
3667
                        new Criterion\ParentLocationId($designLocationId),
3668
                        new Criterion\Location\IsMainLocation(
3669
                            Criterion\Location\IsMainLocation::NOT_MAIN
3670
                        ),
3671
                    ]
3672
                ),
3673
                'offset' => 0,
3674
                'limit' => 10,
3675
                'sortClauses' => [],
3676
            ]
3677
        );
3678
3679
        $searchService = $repository->getSearchService();
3680
        $result = $searchService->findLocations($query);
3681
3682
        $this->assertEquals(1, $result->totalCount);
3683
        $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...
3684
    }
3685
3686
    /**
3687
     * Test for the findLocations() method.
3688
     *
3689
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3690
     */
3691
    public function testSortMainLocationAscending()
3692
    {
3693
        $plainSiteLocationId = 56;
3694
        $designLocationId = 58;
3695
        $partnersContentId = 59;
3696
        $repository = $this->getRepository();
3697
        $locationService = $repository->getLocationService();
3698
        $contentService = $repository->getContentService();
3699
3700
        // Add secondary Location for "Partners" user group, under "Design" page
3701
        $newLocation = $locationService->createLocation(
3702
            $contentService->loadContentInfo($partnersContentId),
3703
            $locationService->newLocationCreateStruct($designLocationId)
3704
        );
3705
3706
        $this->refreshSearch($repository);
3707
3708
        $query = new LocationQuery(
3709
            [
3710
                'filter' => new Criterion\ParentLocationId($designLocationId),
3711
                'offset' => 0,
3712
                'limit' => 10,
3713
                'sortClauses' => [
3714
                    new SortClause\Location\IsMainLocation(
3715
                        LocationQuery::SORT_ASC
3716
                    ),
3717
                ],
3718
            ]
3719
        );
3720
3721
        $searchService = $repository->getSearchService();
3722
        $result = $searchService->findLocations($query);
3723
3724
        $this->assertEquals(2, $result->totalCount);
3725
        $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...
3726
        $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...
3727
    }
3728
3729
    /**
3730
     * Test for the findLocations() method.
3731
     *
3732
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3733
     */
3734
    public function testSortMainLocationDescending()
3735
    {
3736
        $plainSiteLocationId = 56;
3737
        $designLocationId = 58;
3738
        $partnersContentId = 59;
3739
        $repository = $this->getRepository();
3740
        $locationService = $repository->getLocationService();
3741
        $contentService = $repository->getContentService();
3742
3743
        // Add secondary Location for "Partners" user group, under "Design" page
3744
        $newLocation = $locationService->createLocation(
3745
            $contentService->loadContentInfo($partnersContentId),
3746
            $locationService->newLocationCreateStruct($designLocationId)
3747
        );
3748
3749
        $this->refreshSearch($repository);
3750
3751
        $query = new LocationQuery(
3752
            [
3753
                'filter' => new Criterion\ParentLocationId($designLocationId),
3754
                'offset' => 0,
3755
                'limit' => 10,
3756
                'sortClauses' => [
3757
                    new SortClause\Location\IsMainLocation(
3758
                        LocationQuery::SORT_DESC
3759
                    ),
3760
                ],
3761
            ]
3762
        );
3763
3764
        $searchService = $repository->getSearchService();
3765
        $result = $searchService->findLocations($query);
3766
3767
        $this->assertEquals(2, $result->totalCount);
3768
        $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...
3769
        $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...
3770
    }
3771
3772
    /**
3773
     * Test for the findLocations() method.
3774
     *
3775
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3776
     */
3777
    public function testContentWithMultipleLocations()
3778
    {
3779
        $repository = $this->getRepository();
3780
        $contentService = $repository->getContentService();
3781
        $contentTypeService = $repository->getContentTypeService();
3782
        $locationService = $repository->getLocationService();
3783
3784
        $forumType = $contentTypeService->loadContentTypeByIdentifier('forum');
3785
3786
        $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB');
3787
        $createStruct->alwaysAvailable = false;
3788
        $createStruct->setField('name', 'An awesome duplicate forum');
3789
3790
        $draft = $contentService->createContent($createStruct);
3791
        $content = $contentService->publishVersion($draft->getVersionInfo());
3792
3793
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
3794
        $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3795
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5);
3796
        $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3797
3798
        $this->refreshSearch($repository);
3799
3800
        $query = new LocationQuery(
3801
            [
3802
                'filter' => new Criterion\ContentId($content->id),
3803
            ]
3804
        );
3805
3806
        $searchService = $repository->getSearchService();
3807
        $result = $searchService->findLocations($query);
3808
3809
        $this->assertEquals(2, $result->totalCount);
3810
        $locationIds = array_map(
3811
            static function (SearchHit $searchHit): int {
3812
                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...
3813
            },
3814
            $result->searchHits
3815
        );
3816
        $this->assertContains($location1->id, $locationIds);
3817
        $this->assertContains($location2->id, $locationIds);
3818
    }
3819
3820
    protected function createContentForTestUserMetadataGroupHorizontal()
3821
    {
3822
        $repository = $this->getRepository();
3823
        $contentService = $repository->getContentService();
3824
        $contentTypeService = $repository->getContentTypeService();
3825
        $locationService = $repository->getLocationService();
3826
        $userService = $repository->getUserService();
3827
        $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...
3828
        // ID of the "Administrators" user group in an eZ Publish demo installation
3829
        $administratorsUserGroupId = 12;
3830
        // ID of the "Editors" user group in an eZ Publish demo installation
3831
        $editorsUserGroupId = 13;
3832
3833
        $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId);
3834
        $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId);
3835
3836
        // Add additional Location for Administrators UserGroup under Editors UserGroup Location
3837
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3838
            $editorsUserGroup->contentInfo->mainLocationId
3839
        );
3840
        $newAdministratorsUserGroupLocation = $locationService->createLocation(
3841
            $administratorsUserGroup->contentInfo,
3842
            $locationCreateStruct
3843
        );
3844
3845
        // Add additional Location for administrator user under newly created UserGroup Location
3846
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3847
            $newAdministratorsUserGroupLocation->id
3848
        );
3849
        $locationService->createLocation(
3850
            $administratorUser->contentInfo,
3851
            $locationCreateStruct
3852
        );
3853
3854
        // Create a Content to be found through Editors UserGroup id.
3855
        // This ensures data is indexed, it could also be done by updating metadata of
3856
        // an existing Content, but slot would need to reindex Content and that should
3857
        // be tested elsewhere (dedicated indexing integration tests, missing ATM).
3858
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
3859
3860
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3861
        $createStruct->setField('name', 'test');
3862
3863
        $locationCreateStruct = $locationService->newLocationCreateStruct(2);
3864
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
3865
        $content = $contentService->publishVersion($draft->getVersionInfo());
3866
        $contentTypeService->createContentTypeDraft($contentType);
3867
3868
        $this->refreshSearch($repository);
3869
3870
        return $content;
3871
    }
3872
3873
    /**
3874
     * Test for the findContent() method.
3875
     *
3876
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3877
     */
3878
    public function testUserMetadataGroupHorizontalFilterContent($queryType = null)
3879
    {
3880
        if ($queryType === null) {
3881
            $queryType = 'filter';
3882
        }
3883
3884
        $repository = $this->getRepository();
3885
        $searchService = $repository->getSearchService();
3886
        $editorsUserGroupId = 13;
3887
3888
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3889
3890
        $criteria = [];
3891
        $setupFactory = $this->getSetupFactory();
3892
3893
        // Do not limit for LSE, as it does not not require reindexing.
3894
        // See explanation below.
3895
        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...
3896
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3897
        }
3898
3899
        $criteria[] = new Criterion\UserMetadata(
3900
            Criterion\UserMetadata::GROUP,
3901
            Criterion\Operator::EQ,
3902
            $editorsUserGroupId
3903
        );
3904
3905
        $query = new Query(
3906
            [
3907
                $queryType => new Criterion\LogicalAnd($criteria),
3908
                'sortClauses' => [
3909
                    new SortClause\ContentId(),
3910
                ],
3911
                'limit' => 50,
3912
            ]
3913
        );
3914
3915
        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...
3916
            $result = $searchService->findContent($query);
3917
3918
            // Administrator User is owned by itself, when additional Locations are added
3919
            // it should be reindexed and its UserGroups will updated, which means it should
3920
            // also be found as a Content of Editors UserGroup. However we do not handle this
3921
            // in slots yet, and also miss SPI methods to do it without using Search (also
3922
            // needed to decouple services), because as indexing is asynchronous Search
3923
            // should not eat its own dog food for reindexing.
3924
            $this->assertEquals(1, $result->totalCount);
3925
3926
            $this->assertEquals(
3927
                $content->id,
3928
                $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...
3929
            );
3930
        } else {
3931
            // This is how it should eventually work for all search engines,
3932
            // with required reindexing slots properly implemented.
3933
3934
            $result = $searchService->findContent($query);
3935
3936
            // Assert last hit manually, as id will change because it is created in test
3937
            // and not present it base fixture.
3938
            $foundContent1 = array_pop($result->searchHits);
3939
            $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...
3940
            $this->assertEquals($content->id, $foundContent1->valueObject->id);
3941
3942
            $this->simplifySearchResult($result);
3943
            $this->assertEquals(
3944
                include $this->getFixtureDir() . '/UserMetadata.php',
3945
                $result,
3946
                'Search results do not match.',
3947
                .1 // Be quite generous regarding delay -- most important for scores
3948
            );
3949
        }
3950
    }
3951
3952
    /**
3953
     * Test for the findContent() method.
3954
     *
3955
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3956
     */
3957
    public function testUserMetadataGroupHorizontalQueryContent()
3958
    {
3959
        $this->testUserMetadataGroupHorizontalFilterContent('query');
3960
    }
3961
3962
    /**
3963
     * Test for the findLocations() method.
3964
     *
3965
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3966
     */
3967
    public function testUserMetadataGroupHorizontalFilterLocation($queryType = null)
3968
    {
3969
        if ($queryType === null) {
3970
            $queryType = 'filter';
3971
        }
3972
3973
        $repository = $this->getRepository();
3974
        $searchService = $repository->getSearchService();
3975
        $editorsUserGroupId = 13;
3976
3977
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3978
3979
        $criteria = [];
3980
        $setupFactory = $this->getSetupFactory();
3981
3982
        // Do not limit for LSE, as it does not not require reindexing.
3983
        // See explanation below.
3984
        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...
3985
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3986
        }
3987
3988
        $criteria[] = new Criterion\UserMetadata(
3989
            Criterion\UserMetadata::GROUP,
3990
            Criterion\Operator::EQ,
3991
            $editorsUserGroupId
3992
        );
3993
3994
        $query = new LocationQuery(
3995
            [
3996
                $queryType => new Criterion\LogicalAnd($criteria),
3997
                'sortClauses' => [
3998
                    new SortClause\Location\Id(),
3999
                ],
4000
                'limit' => 50,
4001
            ]
4002
        );
4003
4004
        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...
4005
            $result = $searchService->findLocations($query);
4006
4007
            // Administrator User is owned by itself, when additional Locations are added
4008
            // it should be reindexed and its UserGroups will updated, which means it should
4009
            // also be found as a Content of Editors UserGroup. However we do not handle this
4010
            // in slots yet, and also miss SPI methods to do it without using Search (also
4011
            // needed to decouple services), because as indexing is asynchronous Search
4012
            // should not eat its own dog food for reindexing.
4013
            $this->assertEquals(1, $result->totalCount);
4014
4015
            $this->assertEquals(
4016
                $content->contentInfo->mainLocationId,
4017
                $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...
4018
            );
4019
        } else {
4020
            // This is how it should eventually work for all search engines,
4021
            // with required reindexing slots properly implemented.
4022
4023
            $result = $searchService->findLocations($query);
4024
4025
            // Assert last two hits manually, as ids will change because they are created
4026
            // in test and not present in base fixture.
4027
            $foundLocation1 = array_pop($result->searchHits);
4028
            $foundLocation2 = array_pop($result->searchHits);
4029
            // Remove additional Administrators UserGroup Location
4030
            array_pop($result->searchHits);
4031
            $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...
4032
            $this->assertEquals(
4033
                $content->versionInfo->contentInfo->mainLocationId,
4034
                $foundLocation1->valueObject->id
4035
            );
4036
            $this->assertEquals(
4037
                $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...
4038
                $foundLocation2->valueObject->contentId
4039
            );
4040
4041
            $this->simplifySearchResult($result);
4042
            $this->assertEquals(
4043
                include $this->getFixtureDir() . '/UserMetadataLocation.php',
4044
                $result,
4045
                'Search results do not match.',
4046
                .1 // Be quite generous regarding delay -- most important for scores
4047
            );
4048
        }
4049
    }
4050
4051
    /**
4052
     * Test for the findLocations() method.
4053
     *
4054
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4055
     */
4056
    public function testUserMetadataGroupHorizontalQueryLocation()
4057
    {
4058
        $this->testUserMetadataGroupHorizontalFilterLocation('query');
4059
    }
4060
4061
    /**
4062
     * Test for FullText on the findContent() method.
4063
     *
4064
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4065
     */
4066
    public function testFullTextOnNewContent()
4067
    {
4068
        $repository = $this->getRepository();
4069
        $contentService = $repository->getContentService();
4070
        $contentTypeService = $repository->getContentTypeService();
4071
        $locationService = $repository->getLocationService();
4072
        $searchService = $repository->getSearchService();
4073
4074
        $contentCreateStruct = $contentService->newContentCreateStruct(
4075
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4076
            'eng-GB'
4077
        );
4078
4079
        $contentCreateStruct->setField('name', 'foxes');
4080
4081
        $englishContent = $contentService->publishVersion(
4082
            $contentService->createContent(
4083
                $contentCreateStruct,
4084
                [$locationService->newLocationCreateStruct(2)]
4085
            )->versionInfo
4086
        );
4087
4088
        $this->refreshSearch($repository);
4089
4090
        $query = new Query(
4091
            [
4092
                'query' => new Criterion\FullText('foxes'),
4093
            ]
4094
        );
4095
4096
        $searchResult = $searchService->findContentInfo($query);
4097
4098
        $this->assertEquals(1, $searchResult->totalCount);
4099
        $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...
4100
    }
4101
4102
    /**
4103
     * Test for the findContent() method.
4104
     *
4105
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4106
     */
4107
    public function testLanguageAnalysisSeparateContent()
4108
    {
4109
        $setupFactory = $this->getSetupFactory();
4110
        if (!$setupFactory instanceof LegacyElasticsearch) {
4111
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4112
        }
4113
4114
        $repository = $this->getRepository();
4115
        $contentService = $repository->getContentService();
4116
        $contentTypeService = $repository->getContentTypeService();
4117
        $locationService = $repository->getLocationService();
4118
        $searchService = $repository->getSearchService();
4119
        $languageService = $repository->getContentLanguageService();
4120
4121
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4122
        $languageCreateStruct->languageCode = 'rus-RU';
4123
        $languageCreateStruct->name = 'Russian';
4124
4125
        $languageService->createLanguage($languageCreateStruct);
4126
4127
        $contentCreateStruct = $contentService->newContentCreateStruct(
4128
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4129
            'eng-GB'
4130
        );
4131
4132
        $contentCreateStruct->setField('name', 'foxes');
4133
4134
        $englishContent = $contentService->publishVersion(
4135
            $contentService->createContent(
4136
                $contentCreateStruct,
4137
                [$locationService->newLocationCreateStruct(2)]
4138
            )->versionInfo
4139
        );
4140
4141
        $contentCreateStruct = $contentService->newContentCreateStruct(
4142
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4143
            'rus-RU'
4144
        );
4145
4146
        $contentCreateStruct->setField('name', 'foxes');
4147
4148
        $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...
4149
            $contentService->createContent(
4150
                $contentCreateStruct,
4151
                [$locationService->newLocationCreateStruct(2)]
4152
            )->versionInfo
4153
        );
4154
4155
        // Only Content in English should be found, because Content in Russian
4156
        // will not be correctly stemmed
4157
        $query = new Query(
4158
            [
4159
                'query' => new Criterion\FullText('foxing'),
4160
            ]
4161
        );
4162
4163
        $searchResult = $searchService->findContent($query);
4164
4165
        $this->assertEquals(1, $searchResult->totalCount);
4166
        $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...
4167
    }
4168
4169
    /**
4170
     * Test for the findContent() method.
4171
     *
4172
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4173
     */
4174
    public function testLanguageAnalysisSameContent()
4175
    {
4176
        $setupFactory = $this->getSetupFactory();
4177
        if (!$setupFactory instanceof LegacyElasticsearch) {
4178
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4179
        }
4180
4181
        $repository = $this->getRepository();
4182
        $contentService = $repository->getContentService();
4183
        $contentTypeService = $repository->getContentTypeService();
4184
        $locationService = $repository->getLocationService();
4185
        $searchService = $repository->getSearchService();
4186
        $languageService = $repository->getContentLanguageService();
4187
4188
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4189
        $languageCreateStruct->languageCode = 'rus-RU';
4190
        $languageCreateStruct->name = 'Russian';
4191
4192
        $languageService->createLanguage($languageCreateStruct);
4193
4194
        $contentCreateStruct = $contentService->newContentCreateStruct(
4195
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4196
            'eng-GB'
4197
        );
4198
4199
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4200
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4201
4202
        $mixedContent = $contentService->publishVersion(
4203
            $contentService->createContent(
4204
                $contentCreateStruct,
4205
                [$locationService->newLocationCreateStruct(2)]
4206
            )->versionInfo
4207
        );
4208
4209
        // Content will be found because translation in Russian will be correctly stemmed
4210
        $query = new Query(
4211
            [
4212
                'query' => new Criterion\FullText('важнее'),
4213
            ]
4214
        );
4215
4216
        $searchResult = $searchService->findContent($query);
4217
4218
        $this->assertEquals(1, $searchResult->totalCount);
4219
        $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...
4220
    }
4221
4222
    /**
4223
     * Test for the findContent() method.
4224
     *
4225
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4226
     */
4227
    public function testLanguageAnalysisSameContentNotFound()
4228
    {
4229
        $setupFactory = $this->getSetupFactory();
4230
        if (!$setupFactory instanceof LegacyElasticsearch) {
4231
            $this->markTestSkipped('Language analysis is implemented only for Elasticsearch storage');
4232
        }
4233
4234
        $repository = $this->getRepository();
4235
        $contentService = $repository->getContentService();
4236
        $contentTypeService = $repository->getContentTypeService();
4237
        $locationService = $repository->getLocationService();
4238
        $searchService = $repository->getSearchService();
4239
        $languageService = $repository->getContentLanguageService();
4240
4241
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4242
        $languageCreateStruct->languageCode = 'rus-RU';
4243
        $languageCreateStruct->name = 'Russian';
4244
4245
        $languageService->createLanguage($languageCreateStruct);
4246
4247
        $contentCreateStruct = $contentService->newContentCreateStruct(
4248
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4249
            'eng-GB'
4250
        );
4251
4252
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4253
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4254
4255
        $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...
4256
            $contentService->createContent(
4257
                $contentCreateStruct,
4258
                [$locationService->newLocationCreateStruct(2)]
4259
            )->versionInfo
4260
        );
4261
4262
        // Content should be found because translation in Russian will be correctly stemmed
4263
        $query = new Query(
4264
            [
4265
                'query' => new Criterion\FullText('важнее'),
4266
            ]
4267
        );
4268
4269
        // Filtering fields for only English will cause no match because the term will
4270
        // not be correctly stemmed
4271
        $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]);
4272
4273
        $this->assertEquals(0, $searchResult->totalCount);
4274
    }
4275
4276
    /**
4277
     * Test for the findContent() method searching for content filtered by languages.
4278
     *
4279
     * @covers \eZ\Publish\Core\Repository\SearchService::findContent
4280
     */
4281
    public function testFindContentWithLanguageFilter()
4282
    {
4283
        $repository = $this->getRepository();
4284
        $searchService = $repository->getSearchService();
4285
4286
        $query = new Query(
4287
            [
4288
                'filter' => new Criterion\ContentId([4]),
4289
                'offset' => 0,
4290
            ]
4291
        );
4292
        $searchResult = $searchService->findContent(
4293
            $query,
4294
            ['languages' => ['eng-US']],
4295
            false
4296
        );
4297
        /* END: Use Case */
4298
4299
        $this->assertInstanceOf(
4300
            SearchResult::class,
4301
            $searchResult
4302
        );
4303
4304
        $this->assertEquals(1, $searchResult->totalCount);
4305
        $this->assertCount($searchResult->totalCount, $searchResult->searchHits);
4306
        foreach ($searchResult->searchHits as $searchHit) {
4307
            $this->assertInstanceOf(
4308
                SearchHit::class,
4309
                $searchHit
4310
            );
4311
        }
4312
    }
4313
4314
    /**
4315
     * This test prepares data for other tests.
4316
     *
4317
     * @see testFulltextContentSearchComplex
4318
     * @see testFulltextLocationSearchComplex
4319
     *
4320
     * @return array
4321
     */
4322
    public function testFulltextComplex()
4323
    {
4324
        $repository = $this->getRepository();
4325
        $contentService = $repository->getContentService();
4326
        $contentTypeService = $repository->getContentTypeService();
4327
        $locationService = $repository->getLocationService();
4328
4329
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
4330
        $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4331
4332
        $contentCreateStruct->setField('name', 'red');
4333
        $contentCreateStruct->setField('short_name', 'red apple');
4334
        $content1 = $contentService->publishVersion(
4335
            $contentService->createContent(
4336
                $contentCreateStruct,
4337
                [$locationService->newLocationCreateStruct(2)]
4338
            )->versionInfo
4339
        );
4340
4341
        $contentCreateStruct->setField('name', 'apple');
4342
        $contentCreateStruct->setField('short_name', 'two');
4343
        $content2 = $contentService->publishVersion(
4344
            $contentService->createContent(
4345
                $contentCreateStruct,
4346
                [$locationService->newLocationCreateStruct(2)]
4347
            )->versionInfo
4348
        );
4349
4350
        $contentCreateStruct->setField('name', 'red apple');
4351
        $contentCreateStruct->setField('short_name', 'three');
4352
        $content3 = $contentService->publishVersion(
4353
            $contentService->createContent(
4354
                $contentCreateStruct,
4355
                [$locationService->newLocationCreateStruct(2)]
4356
            )->versionInfo
4357
        );
4358
4359
        $contentCreateStruct->setField('name', 'four');
4360
        $contentCreateStruct->setField('name', 'german red apple', 'ger-DE');
4361
        $contentCreateStruct->setField('short_name', 'four');
4362
        $contentCreateStruct->setField('short_name', 'german red apple', 'ger-DE');
4363
        $contentService->publishVersion(
4364
            $contentService->createContent(
4365
                $contentCreateStruct,
4366
                [$locationService->newLocationCreateStruct(2)]
4367
            )->versionInfo
4368
        );
4369
4370
        $this->refreshSearch($repository);
4371
4372
        $criterion = new Criterion\FullText(
4373
            'red apple',
4374
            [
4375
                'boost' => [
4376
                    'short_name' => 2,
4377
                ],
4378
                'fuzziness' => .1,
4379
            ]
4380
        );
4381
4382
        return [$criterion, $content1, $content2, $content3];
4383
    }
4384
4385
    /**
4386
     * Test for the findContent() method.
4387
     *
4388
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4389
     * @depends testFulltextComplex
4390
     *
4391
     * @param array $data
4392
     */
4393
    public function testFulltextContentSearchComplex(array $data)
4394
    {
4395
        // Do not initialize from scratch
4396
        $repository = $this->getRepository(false);
4397
        $searchService = $repository->getSearchService();
4398
        list($criterion, $content1, $content2, $content3) = $data;
4399
4400
        $searchResult = $searchService->findContent(
4401
            new Query(['query' => $criterion]),
4402
            ['languages' => ['eng-GB']]
4403
        );
4404
        $searchHits = $searchResult->searchHits;
4405
4406
        $this->assertEquals(3, $searchResult->totalCount);
4407
4408
        // Legacy search engine does have scoring, sorting the results by ID in that case
4409
        $setupFactory = $this->getSetupFactory();
4410
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
4411
            $this->sortSearchHitsById($searchHits);
4412
4413
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
4414
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->id);
4415
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->id);
4416
4417
            return;
4418
        }
4419
4420
        // Assert scores are descending
4421
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4422
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4423
4424
        // Assert order
4425
        $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...
4426
        $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...
4427
        $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...
4428
    }
4429
4430
    /**
4431
     * Test for the findContent() method.
4432
     *
4433
     * @covers \eZ\Publish\API\Repository\SearchService::findContent()
4434
     * @depends testFulltextComplex
4435
     *
4436
     * @param array $data
4437
     */
4438
    public function testFulltextContentTranslationSearch(array $data)
4439
    {
4440
        $criterion = $data[0];
4441
        $query = new Query(['query' => $criterion]);
4442
4443
        $this->assertFulltextSearchForTranslations(self::FIND_CONTENT_METHOD, $query);
4444
    }
4445
4446
    /**
4447
     * Test for the findLocations() method.
4448
     *
4449
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4450
     * @depends testFulltextComplex
4451
     *
4452
     * @param array $data
4453
     */
4454
    public function testFulltextLocationSearchComplex(array $data)
4455
    {
4456
        $setupFactory = $this->getSetupFactory();
4457
        if ($setupFactory instanceof LegacyElasticsearch) {
4458
            $this->markTestIncomplete(
4459
                'Fulltext criterion is not supported with Location search in Elasticsearch engine'
4460
            );
4461
        }
4462
4463
        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...
4464
            $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!');
4465
        }
4466
4467
        // Do not initialize from scratch
4468
        $repository = $this->getRepository(false);
4469
        list($criterion, $content1, $content2, $content3) = $data;
4470
        $searchService = $repository->getSearchService();
4471
4472
        $searchResult = $searchService->findLocations(
4473
            new LocationQuery(['query' => $criterion]),
4474
            ['languages' => ['eng-GB']]
4475
        );
4476
        $searchHits = $searchResult->searchHits;
4477
4478
        $this->assertEquals(3, $searchResult->totalCount);
4479
4480
        // Legacy search engine does have scoring, sorting the results by ID in that case
4481
        $setupFactory = $this->getSetupFactory();
4482
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
4483
            $this->sortSearchHitsById($searchHits);
4484
4485
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
4486
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId);
4487
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId);
4488
4489
            return;
4490
        }
4491
4492
        // Assert scores are descending
4493
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4494
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4495
4496
        // Assert order
4497
        $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...
4498
        $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...
4499
        $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...
4500
    }
4501
4502
    /**
4503
     * Test for the findLocations() method.
4504
     *
4505
     * @covers \eZ\Publish\API\Repository\SearchService::findLocations()
4506
     * @depends testFulltextComplex
4507
     *
4508
     * @param array $data
4509
     */
4510
    public function testFulltextLocationTranslationSearch(array $data): void
4511
    {
4512
        $criterion = $data[0];
4513
        $query = new LocationQuery(['query' => $criterion]);
4514
4515
        $this->assertFulltextSearchForTranslations(self::FIND_LOCATION_METHOD, $query);
4516
    }
4517
4518
    /**
4519
     * Assert that query result matches the given fixture.
4520
     *
4521
     * @throws \ErrorException
4522
     * @throws \ReflectionException
4523
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
4524
     */
4525
    protected function assertQueryFixture(
4526
        Query $query,
4527
        string $fixtureFilePath,
4528
        ?callable $closure = null,
4529
        bool $ignoreScore = true,
4530
        bool $info = false,
4531
        bool $id = true
4532
    ): void {
4533
        $repository = $this->getRepository();
4534
        $searchService = $repository->getSearchService();
4535
4536
        try {
4537
            if ($query instanceof LocationQuery) {
4538
                $setupFactory = $this->getSetupFactory();
4539
                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...
4540
                    // @todo When we want to test score again by default we will need fixtures for Solr
4541
                }
4542
4543
                if ($setupFactory instanceof LegacyElasticsearch) {
4544
                    $position = strrpos($fixtureFilePath, '/');
4545
                    $fixtureFilePath = substr_replace($fixtureFilePath, '/Location', $position, 0);
4546
                }
4547
4548
                $result = $searchService->findLocations($query);
4549
            } elseif ($query instanceof Query) {
4550
                if ($info) {
4551
                    $result = $searchService->findContentInfo($query);
4552
                } else {
4553
                    $result = $searchService->findContent($query);
4554
                }
4555
            } else {
4556
                $this->fail('Expected instance of LocationQuery or Query, got: ' . gettype($query));
4557
            }
4558
            $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...
4559
        } catch (NotImplementedException $e) {
4560
            $this->markTestSkipped(
4561
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4562
            );
4563
        }
4564
4565
        if (!is_file($fixtureFilePath)) {
4566
            if (isset($_ENV['ez_tests_record'])) {
4567
                file_put_contents(
4568
                    $record = $fixtureFilePath . '.recording',
4569
                    "<?php\n\nreturn " . var_export($result, true) . ";\n\n"
4570
                );
4571
                $this->markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result));
4572
            } else {
4573
                $this->markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixtureFilePath);
4574
            }
4575
        }
4576
4577
        $fixture = require $fixtureFilePath;
4578
4579
        if ($closure !== null) {
4580
            $closure($fixture);
4581
            $closure($result);
4582
        }
4583
4584
        if ($ignoreScore) {
4585
            foreach ([$fixture, $result] as $set) {
4586
                $property = new \ReflectionProperty(get_class($set), 'maxScore');
4587
                $property->setAccessible(true);
4588
                $property->setValue($set, 0.0);
4589
4590
                foreach ($set->searchHits as $hit) {
4591
                    $property = new \ReflectionProperty(get_class($hit), 'score');
4592
                    $property->setAccessible(true);
4593
                    $property->setValue($hit, 0.0);
4594
                }
4595
            }
4596
        }
4597
4598
        foreach ([$fixture, $result] as $set) {
4599
            foreach ($set->searchHits as $hit) {
4600
                $property = new \ReflectionProperty(get_class($hit), 'index');
4601
                $property->setAccessible(true);
4602
                $property->setValue($hit, null);
4603
4604
                $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation');
4605
                $property->setAccessible(true);
4606
                $property->setValue($hit, null);
4607
4608
                if (!$id) {
4609
                    $hit->valueObject['id'] = null;
4610
                }
4611
            }
4612
        }
4613
4614
        $this->assertEquals(
4615
            $fixture,
4616
            $result,
4617
            'Search results do not match the fixture: ' . $fixtureFilePath,
4618
            .99 // Be quite generous regarding delay -- most important for scores
4619
        );
4620
    }
4621
4622
    /**
4623
     * Show a simplified view of the search result for manual introspection.
4624
     *
4625
     * @param SearchResult $result
4626
     *
4627
     * @return string
4628
     */
4629
    protected function printResult(SearchResult $result)
4630
    {
4631
        $printed = '';
4632
        foreach ($result->searchHits as $hit) {
4633
            $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']);
4634
        }
4635
4636
        return $printed;
4637
    }
4638
4639
    /**
4640
     * Simplify search result.
4641
     *
4642
     * This leads to saner comparisons of results, since we do not get the full
4643
     * content objects every time.
4644
     *
4645
     * @param SearchResult $result
4646
     */
4647
    protected function simplifySearchResult(SearchResult $result)
4648
    {
4649
        $result->time = 1;
4650
4651
        foreach ($result->searchHits as $hit) {
4652
            switch (true) {
4653
                case $hit->valueObject instanceof Content:
4654
                case $hit->valueObject instanceof Location:
4655
                    $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...
4656
                        'id' => $hit->valueObject->contentInfo->id,
4657
                        'title' => $hit->valueObject->contentInfo->name,
4658
                    ];
4659
                    break;
4660
4661
                case $hit->valueObject instanceof ContentInfo:
4662
                    $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...
4663
                        'id' => $hit->valueObject->id,
4664
                        'title' => $hit->valueObject->name,
4665
                    ];
4666
                    break;
4667
4668
                default:
4669
                    throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject));
4670
            }
4671
        }
4672
    }
4673
4674
    /**
4675
     * Get fixture directory.
4676
     *
4677
     * @return string
4678
     */
4679
    protected function getFixtureDir()
4680
    {
4681
        return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/';
4682
    }
4683
4684
    /**
4685
     * For findContentInfo tests, to reuse fixtures for findContent tests.
4686
     *
4687
     * @param callable|null $closure
4688
     *
4689
     * @return callable
4690
     */
4691
    private function getContentInfoFixtureClosure($closure = null)
4692
    {
4693
        /** @var $data \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */
4694
        return function (&$data) use ($closure) {
4695
            foreach ($data->searchHits as $searchHit) {
4696
                if ($searchHit->valueObject instanceof Content) {
4697
                    $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo();
4698
                }
4699
            }
4700
4701
            if (isset($closure)) {
4702
                $closure($data);
4703
            }
4704
        };
4705
    }
4706
4707
    /**
4708
     * Test searching using Field Criterion where the given Field Identifier exists in
4709
     * both searchable and non-searchable Fields.
4710
     * Number of returned results depends on used storage.
4711
     *
4712
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4713
     */
4714
    public function testFieldCriterionForContentsWithIdenticalFieldIdentifiers()
4715
    {
4716
        $this->createContentWithFieldType(
4717
            'url',
4718
            'title',
4719
            'foo'
4720
        );
4721
        $this->createContentWithFieldType(
4722
            'string',
4723
            'title',
4724
            'foo'
4725
        );
4726
        $query = new Query(
4727
            [
4728
                'query' => new Criterion\Field(
4729
                    'title',
4730
                    Criterion\Operator::EQ,
4731
                    'foo'
4732
                ),
4733
            ]
4734
        );
4735
4736
        $repository = $this->getRepository();
4737
        $searchService = $repository->getSearchService();
4738
        $result = $searchService->findContent($query);
4739
4740
        $this->assertTrue(($result->totalCount === 1 || $result->totalCount === 2));
4741
    }
4742
4743
    private function createContentWithFieldType(
4744
        string $fieldType,
4745
        string $fieldName,
4746
        string $fieldValue
4747
    ) {
4748
        $repository = $this->getRepository();
4749
        $contentTypeService = $repository->getContentTypeService();
4750
        $contentService = $repository->getContentService();
4751
4752
        $createStruct = $contentTypeService->newContentTypeCreateStruct($fieldType . uniqid());
4753
        $createStruct->mainLanguageCode = 'eng-GB';
4754
        $createStruct->remoteId = $fieldType . '-123';
4755
        $createStruct->names = ['eng-GB' => $fieldType];
4756
        $createStruct->creatorId = 14;
4757
        $createStruct->creationDate = new \DateTime();
4758
4759
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct($fieldName, 'ez' . $fieldType);
4760
        $fieldCreate->names = ['eng-GB' => $fieldName];
4761
        $fieldCreate->fieldGroup = 'main';
4762
        $fieldCreate->position = 1;
4763
4764
        $createStruct->addFieldDefinition($fieldCreate);
4765
4766
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
4767
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
4768
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
4769
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
4770
4771
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4772
        $createStruct->remoteId = $fieldType . '-456';
4773
        $createStruct->alwaysAvailable = false;
4774
        $createStruct->setField(
4775
            $fieldName,
4776
            $fieldValue
4777
        );
4778
4779
        $draft = $contentService->createContent($createStruct);
4780
        $content = $contentService->publishVersion($draft->getVersionInfo());
4781
4782
        $this->refreshSearch($repository);
4783
4784
        return $content;
4785
    }
4786
4787
    /**
4788
     * @param string $findMethod
4789
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
4790
     * @param array $languages
4791
     * @param bool $useAlwaysAvailable
4792
     *
4793
     * @throws \InvalidArgumentException
4794
     *
4795
     * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult
4796
     */
4797
    private function find(
4798
        string $findMethod,
4799
        Query $query,
4800
        array $languages,
4801
        bool $useAlwaysAvailable
4802
    ): SearchResult {
4803
        if (false === in_array($findMethod, self::AVAILABLE_FIND_METHODS, true)) {
4804
            throw new InvalidArgumentException(
4805
                'Allowed find methods are: '
4806
                . implode(',', self::AVAILABLE_FIND_METHODS)
4807
            );
4808
        }
4809
4810
        $repository = $this->getRepository(false);
4811
        $searchService = $repository->getSearchService();
4812
4813
        return $searchService->{$findMethod}(
4814
            $query,
4815
            [
4816
                'languages' => $languages,
4817
                'useAlwaysAvailable' => $useAlwaysAvailable,
4818
            ]
4819
        );
4820
    }
4821
4822
    /**
4823
     * @param string $findMethod
4824
     * @param \eZ\Publish\API\Repository\Values\Content\Query $query
4825
     */
4826
    private function assertFulltextSearchForTranslations(string $findMethod, Query $query): void
4827
    {
4828
        /*
4829
         * Search in German translations without always available
4830
         */
4831
        $searchResult = $this->find($findMethod, $query, ['ger-DE'], false);
4832
        $this->assertEquals(1, $searchResult->totalCount);
4833
        $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']);
4834
4835
        /*
4836
         * Search in German translations with always available
4837
         */
4838
        $searchResult = $this->find($findMethod, $query, ['ger-DE'], true);
4839
        $this->assertEquals(4, $searchResult->totalCount);
4840
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']);
4841
4842
        /*
4843
         * Search in multiple (ger-DE, eng-GB) translations without always available
4844
         */
4845
        $searchResult = $this->find($findMethod, $query, ['ger-DE', 'eng-GB'], false);
4846
        $this->assertEquals(4, $searchResult->totalCount);
4847
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']);
4848
4849
        /*
4850
         * Search in multiple (eng-US, ger-DE) translations without always available
4851
         */
4852
        $searchResult = $this->find($findMethod, $query, ['eng-US', 'ger-DE'], false);
4853
        $this->assertEquals(1, $searchResult->totalCount);
4854
        $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']);
4855
4856
        /*
4857
         * Search in eng-US translations without always available
4858
         */
4859
        $searchResult = $this->find($findMethod, $query, ['eng-US'], false);
4860
        $this->assertEquals(0, $searchResult->totalCount);
4861
4862
        /*
4863
         * Search in eng-US translations with always available
4864
         */
4865
        $searchResult = $this->find($findMethod, $query, ['eng-US'], true);
4866
        $this->assertEquals(3, $searchResult->totalCount);
4867
        $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB']);
4868
    }
4869
4870
    /**
4871
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
4872
     * @param string[] $translationsToMatch
4873
     *
4874
     * @throws \InvalidArgumentException
4875
     */
4876
    private function assertSearchResultMatchTranslations(
4877
        SearchResult $searchResult,
4878
        array $translationsToMatch
4879
    ): void {
4880
        $translationsToMatchCount = count($translationsToMatch);
4881
4882
        if ($searchResult->totalCount < $translationsToMatchCount
4883
            || $searchResult->totalCount > $translationsToMatchCount
4884
        ) {
4885
            throw new InvalidArgumentException(
4886
                'Argument `translationsToMatch` must be equal to the search result total count!'
4887
            );
4888
        }
4889
4890
        $searchHits = $searchResult->searchHits;
4891
        $this->sortSearchHitsById($searchHits);
4892
4893
        for ($i = 0; $i < $searchResult->totalCount; ++$i) {
4894
            $this->assertEquals(
4895
                $translationsToMatch[$i],
4896
                $searchHits[$i]->matchedTranslation
4897
            );
4898
        }
4899
    }
4900
4901
    private function sortSearchHitsById(array &$searchHits): void
4902
    {
4903
        usort(
4904
           $searchHits,
4905
           static function (SearchHit $a, SearchHit $b): int {
4906
               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...
4907
           }
4908
        );
4909
    }
4910
4911
    /**
4912
     * @dataProvider providerForTestSortingByNumericFieldsWithValuesOfDifferentLength
4913
     *
4914
     * @param int[] $expectedOrderedIds
4915
     *
4916
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
4917
     */
4918
    public function testSortingByNumericFieldsWithValuesOfDifferentLength(
4919
        LocationQuery $query,
4920
        array $expectedOrderedIds
4921
    ): void {
4922
        $repository = $this->getRepository();
4923
        $searchService = $repository->getSearchService();
4924
4925
        $result = $searchService->findLocations($query);
4926
4927
        self::assertEquals(count($expectedOrderedIds), $result->totalCount);
4928
        $actualIds = array_map(
4929
            static function (SearchHit $searchHit) {
4930
                /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
4931
                $location = $searchHit->valueObject;
4932
4933
                return $location->id;
4934
            },
4935
            $result->searchHits
4936
        );
4937
        self::assertEquals($expectedOrderedIds, $actualIds);
4938
    }
4939
4940
    public function providerForTestSortingByNumericFieldsWithValuesOfDifferentLength(): iterable
4941
    {
4942
        yield 'Location ID ASC' => [
4943
            new LocationQuery(
4944
                [
4945
                    'filter' => new Criterion\LocationId([43, 5]),
4946
                    'sortClauses' => [
4947
                        new SortClause\Location\Id(LocationQuery::SORT_ASC),
4948
                    ],
4949
                ]
4950
            ),
4951
            [5, 43],
4952
        ];
4953
4954
        yield 'Location ID DESC' => [
4955
            new LocationQuery(
4956
                [
4957
                    'filter' => new Criterion\LocationId([5, 43]),
4958
                    'sortClauses' => [
4959
                        new SortClause\Location\Id(LocationQuery::SORT_DESC),
4960
                    ],
4961
                ]
4962
            ),
4963
            [43, 5],
4964
        ];
4965
4966
        yield 'Content ID ASC' => [
4967
            new LocationQuery(
4968
                [
4969
                    'filter' => new Criterion\ContentId([14, 4]),
4970
                    'sortClauses' => [
4971
                        new SortClause\ContentId(LocationQuery::SORT_ASC),
4972
                    ],
4973
                ]
4974
            ),
4975
            // those are still Location IDs as it's LocationQuery
4976
            [5, 15],
4977
        ];
4978
4979
        yield 'Content ID DESC' => [
4980
            new LocationQuery(
4981
                [
4982
                    'filter' => new Criterion\ContentId([4, 14]),
4983
                    'sortClauses' => [
4984
                        new SortClause\ContentId(LocationQuery::SORT_DESC),
4985
                    ],
4986
                ]
4987
            ),
4988
            // those are still Location IDs as it's LocationQuery
4989
            [15, 5],
4990
        ];
4991
4992
        yield 'Content ID DESC' => [
4993
            new LocationQuery(
4994
                [
4995
                    'filter' => new Criterion\ContentId([4, 14]),
4996
                    'sortClauses' => [
4997
                        new SortClause\ContentId(LocationQuery::SORT_DESC),
4998
                    ],
4999
                ]
5000
            ),
5001
            // those are still Location IDs as it's LocationQuery
5002
            [15, 5],
5003
        ];
5004
    }
5005
}
5006