1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the eZ Publish Kernel package. |
5
|
|
|
* |
6
|
|
|
* @copyright Copyright (C) eZ Systems AS. All rights reserved. |
7
|
|
|
* @license For full copyright and license information view LICENSE file distributed with this source code. |
8
|
|
|
* |
9
|
|
|
* @version //autogentag// |
10
|
|
|
*/ |
11
|
|
|
namespace eZ\Publish\API\Repository\Tests; |
12
|
|
|
|
13
|
|
|
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; |
14
|
|
|
use eZ\Publish\API\Repository\Tests\SetupFactory\LegacyElasticsearch as LegacyElasticsearchSetupFactory; |
15
|
|
|
use eZ\Publish\API\Repository\Values\Content\ContentInfo; |
16
|
|
|
use eZ\Publish\API\Repository\Exceptions\NotFoundException; |
17
|
|
|
use eZ\Publish\API\Repository\Values\Content\LocationQuery; |
18
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query; |
19
|
|
|
use eZ\Publish\API\Repository\Values\Content\Query\Criterion; |
20
|
|
|
use DateTime; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Test case for indexing operations with a search engine. |
24
|
|
|
* |
25
|
|
|
* @group integration |
26
|
|
|
* @group search |
27
|
|
|
* @group indexing |
28
|
|
|
*/ |
29
|
|
|
class SearchEngineIndexingTest extends BaseTest |
30
|
|
|
{ |
31
|
|
|
/** |
32
|
|
|
* Test that indexing full text data depends on the isSearchable flag on the field definition. |
33
|
|
|
*/ |
34
|
|
|
public function testFindContentInfoFullTextIsSearchable() |
35
|
|
|
{ |
36
|
|
|
$setupFactory = $this->getSetupFactory(); |
37
|
|
|
if (!$setupFactory instanceof LegacySolrSetupFactory && !$setupFactory instanceof LegacyElasticsearchSetupFactory) { |
|
|
|
|
38
|
|
|
$this->markTestSkipped( |
39
|
|
|
'Legacy Search Engine is missing full text indexing implementation' |
40
|
|
|
); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
$searchTerm = 'pamplemousse'; |
44
|
|
|
$content = $this->createFullTextIsSearchableContent($searchTerm, true); |
45
|
|
|
|
46
|
|
|
$repository = $this->getRepository(); |
47
|
|
|
$searchService = $repository->getSearchService(); |
48
|
|
|
|
49
|
|
|
$query = new Query( |
50
|
|
|
[ |
51
|
|
|
'query' => new Criterion\FullText($searchTerm), |
52
|
|
|
] |
53
|
|
|
); |
54
|
|
|
|
55
|
|
|
$searchResult = $searchService->findContentInfo($query); |
56
|
|
|
|
57
|
|
|
$this->assertEquals(1, $searchResult->totalCount); |
58
|
|
|
$contentInfo = $searchResult->searchHits[0]->valueObject; |
59
|
|
|
$this->assertEquals($content->id, $contentInfo->id); |
|
|
|
|
60
|
|
|
|
61
|
|
|
return $contentInfo; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Test that indexing full text data depends on the isSearchable flag on the field definition. |
66
|
|
|
* |
67
|
|
|
* @depends testFindContentInfoFullTextIsSearchable |
68
|
|
|
* |
69
|
|
|
* @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo |
70
|
|
|
*/ |
71
|
|
|
public function testFindLocationsFullTextIsSearchable(ContentInfo $contentInfo) |
72
|
|
|
{ |
73
|
|
|
$setupFactory = $this->getSetupFactory(); |
74
|
|
|
if ($setupFactory instanceof LegacyElasticsearchSetupFactory) { |
75
|
|
|
$this->markTestSkipped( |
76
|
|
|
'Elasticsearch Search Engine is missing full text Location search implementation' |
77
|
|
|
); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
$searchTerm = 'pamplemousse'; |
81
|
|
|
|
82
|
|
|
$repository = $this->getRepository(false); |
83
|
|
|
$searchService = $repository->getSearchService(); |
84
|
|
|
|
85
|
|
|
$query = new LocationQuery( |
86
|
|
|
[ |
87
|
|
|
'query' => new Criterion\FullText($searchTerm), |
88
|
|
|
] |
89
|
|
|
); |
90
|
|
|
|
91
|
|
|
$searchResult = $searchService->findLocations($query); |
92
|
|
|
|
93
|
|
|
$this->assertEquals(1, $searchResult->totalCount); |
94
|
|
|
$this->assertEquals( |
95
|
|
|
$contentInfo->mainLocationId, |
96
|
|
|
$searchResult->searchHits[0]->valueObject->id |
|
|
|
|
97
|
|
|
); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Test that indexing full text data depends on the isSearchable flag on the field definition. |
102
|
|
|
* |
103
|
|
|
* @depends testFindContentInfoFullTextIsSearchable |
104
|
|
|
*/ |
105
|
|
View Code Duplication |
public function testFindContentInfoFullTextIsNotSearchable() |
106
|
|
|
{ |
107
|
|
|
$searchTerm = 'pamplemousse'; |
108
|
|
|
$this->createFullTextIsSearchableContent($searchTerm, false); |
109
|
|
|
|
110
|
|
|
$repository = $this->getRepository(); |
111
|
|
|
$searchService = $repository->getSearchService(); |
112
|
|
|
|
113
|
|
|
$query = new Query( |
114
|
|
|
[ |
115
|
|
|
'query' => new Criterion\FullText($searchTerm), |
116
|
|
|
] |
117
|
|
|
); |
118
|
|
|
|
119
|
|
|
$searchResult = $searchService->findContentInfo($query); |
120
|
|
|
|
121
|
|
|
$this->assertEquals(0, $searchResult->totalCount); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Test that indexing full text data depends on the isSearchable flag on the field definition. |
126
|
|
|
* |
127
|
|
|
* @depends testFindLocationsFullTextIsSearchable |
128
|
|
|
*/ |
129
|
|
View Code Duplication |
public function testFindLocationsFullTextIsNotSearchable() |
130
|
|
|
{ |
131
|
|
|
$searchTerm = 'pamplemousse'; |
132
|
|
|
|
133
|
|
|
$repository = $this->getRepository(false); |
134
|
|
|
$searchService = $repository->getSearchService(); |
135
|
|
|
|
136
|
|
|
$query = new LocationQuery( |
137
|
|
|
[ |
138
|
|
|
'query' => new Criterion\FullText($searchTerm), |
139
|
|
|
] |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
$searchResult = $searchService->findLocations($query); |
143
|
|
|
|
144
|
|
|
$this->assertEquals(0, $searchResult->totalCount); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Creates Content for testing full text search depending on the isSearchable flag. |
149
|
|
|
* |
150
|
|
|
* @see testFindContentInfoFullTextIsearchable |
151
|
|
|
* @see testFindLocationsFullTextIsSearchable |
152
|
|
|
* @see testFindContentInfoFullTextIsNotSearchable |
153
|
|
|
* @see testFindLocationsFullTextIsNotSearchable |
154
|
|
|
* |
155
|
|
|
* @param string $searchText |
156
|
|
|
* @param bool $isSearchable |
157
|
|
|
* |
158
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Content |
159
|
|
|
*/ |
160
|
|
|
protected function createFullTextIsSearchableContent($searchText, $isSearchable) |
161
|
|
|
{ |
162
|
|
|
$repository = $this->getRepository(); |
163
|
|
|
$contentService = $repository->getContentService(); |
164
|
|
|
$contentTypeService = $repository->getContentTypeService(); |
165
|
|
|
$locationService = $repository->getLocationService(); |
166
|
|
|
$contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); |
167
|
|
|
|
168
|
|
|
if (!$isSearchable) { |
169
|
|
|
$contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); |
170
|
|
|
$fieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); |
171
|
|
|
$fieldDefinitionUpdateStruct->isSearchable = false; |
172
|
|
|
|
173
|
|
|
$fieldDefinition = $contentType->getFieldDefinition('name'); |
174
|
|
|
|
175
|
|
|
$contentTypeService->updateFieldDefinition( |
176
|
|
|
$contentTypeDraft, |
177
|
|
|
$fieldDefinition, |
178
|
|
|
$fieldDefinitionUpdateStruct |
179
|
|
|
); |
180
|
|
|
|
181
|
|
|
$contentTypeService->publishContentTypeDraft($contentTypeDraft); |
182
|
|
|
$contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
$contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); |
186
|
|
|
|
187
|
|
|
$contentCreateStruct->setField('name', $searchText); |
188
|
|
|
$contentCreateStruct->setField('short_name', 'hello world'); |
189
|
|
|
$content = $contentService->publishVersion( |
190
|
|
|
$contentService->createContent( |
191
|
|
|
$contentCreateStruct, |
192
|
|
|
[$locationService->newLocationCreateStruct(2)] |
193
|
|
|
)->versionInfo |
194
|
|
|
); |
195
|
|
|
|
196
|
|
|
$this->refreshSearch($repository); |
197
|
|
|
|
198
|
|
|
return $content; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* EZP-26186: Make sure index is NOT deleted on removal of version draft (affected Solr & content index on Elastic). |
203
|
|
|
*/ |
204
|
|
|
public function testDeleteVersion() |
205
|
|
|
{ |
206
|
|
|
$repository = $this->getRepository(); |
207
|
|
|
$contentService = $repository->getContentService(); |
208
|
|
|
$searchService = $repository->getSearchService(); |
209
|
|
|
|
210
|
|
|
$membersContentId = $this->generateId('content', 11); |
211
|
|
|
$contentInfo = $contentService->loadContentInfo($membersContentId); |
212
|
|
|
|
213
|
|
|
$draft = $contentService->createContentDraft($contentInfo); |
214
|
|
|
$contentService->deleteVersion($draft->getVersionInfo()); |
215
|
|
|
|
216
|
|
|
$this->refreshSearch($repository); |
217
|
|
|
|
218
|
|
|
// Found |
219
|
|
|
$criterion = new Criterion\LocationId($contentInfo->mainLocationId); |
220
|
|
|
$query = new Query(array('filter' => $criterion)); |
221
|
|
|
$result = $searchService->findContentInfo($query); |
222
|
|
|
$this->assertEquals(1, $result->totalCount); |
223
|
|
|
$this->assertEquals( |
224
|
|
|
$contentInfo->id, |
225
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
226
|
|
|
); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* EZP-26186: Make sure affected child locations are deleted on content deletion (affected Solr & Elastic). |
231
|
|
|
*/ |
232
|
|
|
public function testDeleteContent() |
233
|
|
|
{ |
234
|
|
|
$repository = $this->getRepository(); |
235
|
|
|
$contentService = $repository->getContentService(); |
236
|
|
|
$searchService = $repository->getSearchService(); |
237
|
|
|
|
238
|
|
|
$anonymousUsersContentId = $this->generateId('content', 42); |
239
|
|
|
$contentInfo = $contentService->loadContentInfo($anonymousUsersContentId); |
240
|
|
|
|
241
|
|
|
$contentService->deleteContent($contentInfo); |
242
|
|
|
|
243
|
|
|
$this->refreshSearch($repository); |
244
|
|
|
|
245
|
|
|
// Should not be found |
246
|
|
|
$criterion = new Criterion\ParentLocationId($contentInfo->mainLocationId); |
247
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
248
|
|
|
$result = $searchService->findLocations($query); |
249
|
|
|
$this->assertEquals(0, $result->totalCount); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
public function testCreateLocation() |
253
|
|
|
{ |
254
|
|
|
$repository = $this->getRepository(); |
255
|
|
|
$locationService = $repository->getLocationService(); |
256
|
|
|
$contentService = $repository->getContentService(); |
257
|
|
|
$searchService = $repository->getSearchService(); |
258
|
|
|
|
259
|
|
|
$rootLocationId = 2; |
260
|
|
|
$membersContentId = 11; |
261
|
|
|
$membersContentInfo = $contentService->loadContentInfo($membersContentId); |
262
|
|
|
|
263
|
|
|
$locationCreateStruct = $locationService->newLocationCreateStruct($rootLocationId); |
264
|
|
|
$membersLocation = $locationService->createLocation($membersContentInfo, $locationCreateStruct); |
265
|
|
|
|
266
|
|
|
$this->refreshSearch($repository); |
267
|
|
|
|
268
|
|
|
// Found |
269
|
|
|
$criterion = new Criterion\LocationId($membersLocation->id); |
270
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
271
|
|
|
$result = $searchService->findLocations($query); |
272
|
|
|
$this->assertEquals(1, $result->totalCount); |
273
|
|
|
$this->assertEquals( |
274
|
|
|
$membersLocation->id, |
275
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
276
|
|
|
); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
public function testMoveSubtree() |
280
|
|
|
{ |
281
|
|
|
$repository = $this->getRepository(); |
282
|
|
|
$locationService = $repository->getLocationService(); |
283
|
|
|
$contentService = $repository->getContentService(); |
284
|
|
|
$searchService = $repository->getSearchService(); |
285
|
|
|
|
286
|
|
|
$rootLocationId = 2; |
287
|
|
|
$membersContentId = 11; |
288
|
|
|
$adminsContentId = 12; |
289
|
|
|
$editorsContentId = 13; |
290
|
|
|
$membersContentInfo = $contentService->loadContentInfo($membersContentId); |
291
|
|
|
$adminsContentInfo = $contentService->loadContentInfo($adminsContentId); |
292
|
|
|
$editorsContentInfo = $contentService->loadContentInfo($editorsContentId); |
293
|
|
|
|
294
|
|
|
$locationCreateStruct = $locationService->newLocationCreateStruct($rootLocationId); |
295
|
|
|
$membersLocation = $locationService->createLocation($membersContentInfo, $locationCreateStruct); |
296
|
|
|
$editorsLocation = $locationService->createLocation($editorsContentInfo, $locationCreateStruct); |
297
|
|
|
$adminsLocation = $locationService->createLocation( |
298
|
|
|
$adminsContentInfo, |
299
|
|
|
$locationService->newLocationCreateStruct($membersLocation->id) |
300
|
|
|
); |
301
|
|
|
|
302
|
|
|
$this->refreshSearch($repository); |
303
|
|
|
|
304
|
|
|
// Not found under Editors |
305
|
|
|
$criterion = new Criterion\ParentLocationId($editorsLocation->id); |
306
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
307
|
|
|
$result = $searchService->findLocations($query); |
308
|
|
|
$this->assertEquals(0, $result->totalCount); |
309
|
|
|
|
310
|
|
|
// Found under Members |
311
|
|
|
$criterion = new Criterion\ParentLocationId($membersLocation->id); |
312
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
313
|
|
|
$result = $searchService->findLocations($query); |
314
|
|
|
$this->assertEquals(1, $result->totalCount); |
315
|
|
|
$this->assertEquals( |
316
|
|
|
$adminsLocation->id, |
317
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
318
|
|
|
); |
319
|
|
|
|
320
|
|
|
$locationService->moveSubtree($adminsLocation, $editorsLocation); |
321
|
|
|
$this->refreshSearch($repository); |
322
|
|
|
|
323
|
|
|
// Found under Editors |
324
|
|
|
$criterion = new Criterion\ParentLocationId($editorsLocation->id); |
325
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
326
|
|
|
$result = $searchService->findLocations($query); |
327
|
|
|
$this->assertEquals(1, $result->totalCount); |
328
|
|
|
$this->assertEquals( |
329
|
|
|
$adminsLocation->id, |
330
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
331
|
|
|
); |
332
|
|
|
|
333
|
|
|
// Not found under Members |
334
|
|
|
$criterion = new Criterion\ParentLocationId($membersLocation->id); |
335
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
336
|
|
|
$result = $searchService->findLocations($query); |
337
|
|
|
$this->assertEquals(0, $result->totalCount); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Testing that content is indexed even when containing only fields with values |
342
|
|
|
* considered to be empty by the search engine. |
343
|
|
|
*/ |
344
|
|
|
public function testIndexContentWithNullField() |
345
|
|
|
{ |
346
|
|
|
$repository = $this->getRepository(); |
347
|
|
|
$contentService = $repository->getContentService(); |
348
|
|
|
$contentTypeService = $repository->getContentTypeService(); |
349
|
|
|
$searchService = $repository->getSearchService(); |
350
|
|
|
|
351
|
|
|
$createStruct = $contentTypeService->newContentTypeCreateStruct('test-type'); |
352
|
|
|
$createStruct->mainLanguageCode = 'eng-GB'; |
353
|
|
|
$createStruct->names = array('eng-GB' => 'Test type'); |
354
|
|
|
$createStruct->creatorId = 14; |
355
|
|
|
$createStruct->creationDate = new DateTime(); |
356
|
|
|
|
357
|
|
|
$translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct( |
358
|
|
|
'integer', |
359
|
|
|
'ezinteger' |
360
|
|
|
); |
361
|
|
|
$translatableFieldCreate->names = array('eng-GB' => 'Simple translatable integer field'); |
362
|
|
|
$translatableFieldCreate->fieldGroup = 'main'; |
363
|
|
|
$translatableFieldCreate->position = 1; |
364
|
|
|
$translatableFieldCreate->isTranslatable = true; |
365
|
|
|
$translatableFieldCreate->isSearchable = true; |
366
|
|
|
|
367
|
|
|
$createStruct->addFieldDefinition($translatableFieldCreate); |
368
|
|
|
|
369
|
|
|
$contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); |
370
|
|
|
$contentTypeDraft = $contentTypeService->createContentType( |
371
|
|
|
$createStruct, |
372
|
|
|
array($contentGroup) |
373
|
|
|
); |
374
|
|
|
$contentTypeService->publishContentTypeDraft($contentTypeDraft); |
375
|
|
|
$contentType = $contentTypeService->loadContentType($contentTypeDraft->id); |
|
|
|
|
376
|
|
|
|
377
|
|
|
$createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); |
378
|
|
|
$createStruct->alwaysAvailable = false; |
379
|
|
|
$createStruct->mainLanguageCode = 'eng-GB'; |
380
|
|
|
|
381
|
|
|
$draft = $contentService->createContent($createStruct); |
382
|
|
|
$content = $contentService->publishVersion($draft->getVersionInfo()); |
383
|
|
|
|
384
|
|
|
$this->refreshSearch($repository); |
385
|
|
|
|
386
|
|
|
// Found |
387
|
|
|
$criterion = new Criterion\ContentId($content->id); |
388
|
|
|
$query = new Query(array('filter' => $criterion)); |
389
|
|
|
$result = $searchService->findContent($query); |
390
|
|
|
$this->assertEquals(1, $result->totalCount); |
391
|
|
|
$this->assertEquals( |
392
|
|
|
$content->id, |
393
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
394
|
|
|
); |
395
|
|
|
} |
396
|
|
|
|
397
|
|
|
public function testUpdateLocation() |
398
|
|
|
{ |
399
|
|
|
$repository = $this->getRepository(); |
400
|
|
|
$locationService = $repository->getLocationService(); |
401
|
|
|
$searchService = $repository->getSearchService(); |
402
|
|
|
|
403
|
|
|
$rootLocationId = 2; |
404
|
|
|
$locationToUpdate = $locationService->loadLocation($rootLocationId); |
405
|
|
|
|
406
|
|
|
$criterion = new Criterion\LogicalAnd([ |
407
|
|
|
new Criterion\LocationId($rootLocationId), |
408
|
|
|
new Criterion\Location\Priority(Criterion\Operator::GT, 0), |
409
|
|
|
]); |
410
|
|
|
|
411
|
|
|
$query = new LocationQuery(array('filter' => $criterion)); |
412
|
|
|
$result = $searchService->findLocations($query); |
413
|
|
|
|
414
|
|
|
$this->assertEquals(0, $result->totalCount); |
415
|
|
|
|
416
|
|
|
$locationUpdateStruct = $locationService->newLocationUpdateStruct(); |
417
|
|
|
$locationUpdateStruct->priority = 4; |
418
|
|
|
$locationService->updateLocation($locationToUpdate, $locationUpdateStruct); |
419
|
|
|
|
420
|
|
|
$this->refreshSearch($repository); |
421
|
|
|
|
422
|
|
|
$result = $searchService->findLocations($query); |
423
|
|
|
|
424
|
|
|
$this->assertEquals(1, $result->totalCount); |
425
|
|
|
$this->assertEquals( |
426
|
|
|
$locationToUpdate->id, |
427
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
428
|
|
|
); |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* Testing that content will be deleted with all of its subitems but subitems with additional location will stay as |
433
|
|
|
* they are. |
434
|
|
|
*/ |
435
|
|
|
public function testDeleteLocation() |
436
|
|
|
{ |
437
|
|
|
$repository = $this->getRepository(); |
438
|
|
|
$locationService = $repository->getLocationService(); |
439
|
|
|
|
440
|
|
|
$treeContainerContent = $this->createContentWithName('Tree Container', [2]); |
441
|
|
|
$supposeBeDeletedSubItem = $this->createContentWithName( |
442
|
|
|
'Suppose to be deleted sub-item', |
443
|
|
|
[$treeContainerContent->contentInfo->mainLocationId] |
444
|
|
|
); |
445
|
|
|
$supposeSurviveSubItem = $this->createContentWithName( |
446
|
|
|
'Suppose to Survive Item', |
447
|
|
|
[2, $treeContainerContent->contentInfo->mainLocationId] |
448
|
|
|
); |
449
|
|
|
|
450
|
|
|
$treeContainerLocation = $locationService->loadLocation($treeContainerContent->contentInfo->mainLocationId); |
451
|
|
|
|
452
|
|
|
$this->refreshSearch($repository); |
453
|
|
|
|
454
|
|
|
$this->assertContentIdSearch($treeContainerContent->id, 1); |
455
|
|
|
$this->assertContentIdSearch($supposeSurviveSubItem->id, 1); |
456
|
|
|
$this->assertContentIdSearch($supposeBeDeletedSubItem->id, 1); |
457
|
|
|
|
458
|
|
|
$locationService->deleteLocation($treeContainerLocation); |
459
|
|
|
|
460
|
|
|
$this->refreshSearch($repository); |
461
|
|
|
|
462
|
|
|
$this->assertContentIdSearch($supposeSurviveSubItem->id, 1); |
463
|
|
|
$this->assertContentIdSearch($treeContainerContent->id, 0); |
464
|
|
|
$this->assertContentIdSearch($supposeBeDeletedSubItem->id, 0); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* Test that swapping locations affects properly Search Engine Index. |
469
|
|
|
*/ |
470
|
|
|
public function testSwapLocation() |
471
|
|
|
{ |
472
|
|
|
$repository = $this->getRepository(); |
473
|
|
|
$locationService = $repository->getLocationService(); |
474
|
|
|
$searchService = $repository->getSearchService(); |
475
|
|
|
|
476
|
|
|
$content01 = $this->createContentWithName('content01', [2]); |
477
|
|
|
$location01 = $locationService->loadLocation($content01->contentInfo->mainLocationId); |
478
|
|
|
|
479
|
|
|
$content02 = $this->createContentWithName('content02', [2]); |
480
|
|
|
$location02 = $locationService->loadLocation($content02->contentInfo->mainLocationId); |
481
|
|
|
|
482
|
|
|
$locationService->swapLocation($location01, $location02); |
483
|
|
|
$this->refreshSearch($repository); |
484
|
|
|
|
485
|
|
|
// content02 should be at location01 |
486
|
|
|
$criterion = new Criterion\LocationId($location01->id); |
487
|
|
|
$query = new Query(['filter' => $criterion]); |
488
|
|
|
$results = $searchService->findContent($query); |
489
|
|
|
$this->assertEquals(1, $results->totalCount); |
490
|
|
|
$this->assertEquals($content02->id, $results->searchHits[0]->valueObject->id); |
|
|
|
|
491
|
|
|
|
492
|
|
|
// content01 should be at location02 |
493
|
|
|
$criterion = new Criterion\LocationId($location02->id); |
494
|
|
|
$query = new Query(['filter' => $criterion]); |
495
|
|
|
$results = $searchService->findContent($query); |
496
|
|
|
$this->assertEquals(1, $results->totalCount); |
497
|
|
|
$this->assertEquals($content01->id, $results->searchHits[0]->valueObject->id); |
|
|
|
|
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Test that updating Content metadata affects properly Search Engine Index. |
502
|
|
|
*/ |
503
|
|
|
public function testUpdateContentMetadata() |
504
|
|
|
{ |
505
|
|
|
$repository = $this->getRepository(); |
506
|
|
|
$contentService = $repository->getContentService(); |
507
|
|
|
$locationService = $repository->getLocationService(); |
508
|
|
|
$searchService = $repository->getSearchService(); |
509
|
|
|
|
510
|
|
|
$publishedContent = $this->createContentWithName('updateMetadataTest', [2]); |
511
|
|
|
$newLocationCreateStruct = $locationService->newLocationCreateStruct(60); |
512
|
|
|
$newLocation = $locationService->createLocation($publishedContent->contentInfo, $newLocationCreateStruct); |
513
|
|
|
|
514
|
|
|
$newContentMetadataUpdateStruct = $contentService->newContentMetadataUpdateStruct(); |
515
|
|
|
$newContentMetadataUpdateStruct->remoteId = md5('Test'); |
516
|
|
|
$newContentMetadataUpdateStruct->publishedDate = new \DateTime(); |
517
|
|
|
$newContentMetadataUpdateStruct->publishedDate->add(new \DateInterval('P1D')); |
518
|
|
|
$newContentMetadataUpdateStruct->mainLocationId = $newLocation->id; |
519
|
|
|
|
520
|
|
|
$contentService->updateContentMetadata($publishedContent->contentInfo, $newContentMetadataUpdateStruct); |
521
|
|
|
$this->refreshSearch($repository); |
522
|
|
|
|
523
|
|
|
// find Content by Id, calling findContentInfo which is using the Search Index |
524
|
|
|
$criterion = new Criterion\ContentId($publishedContent->id); |
525
|
|
|
$query = new Query(['filter' => $criterion]); |
526
|
|
|
$results = $searchService->findContentInfo($query); |
527
|
|
|
$this->assertEquals(1, $results->totalCount); |
528
|
|
|
$this->assertEquals($publishedContent->contentInfo->id, $results->searchHits[0]->valueObject->id); |
|
|
|
|
529
|
|
|
|
530
|
|
|
// find Content using updated RemoteId |
531
|
|
|
$criterion = new Criterion\RemoteId($newContentMetadataUpdateStruct->remoteId); |
532
|
|
|
$query = new Query(['filter' => $criterion]); |
533
|
|
|
$results = $searchService->findContent($query); |
534
|
|
|
$this->assertEquals(1, $results->totalCount); |
535
|
|
|
$foundContentInfo = $results->searchHits[0]->valueObject->contentInfo; |
|
|
|
|
536
|
|
|
/** @var \eZ\Publish\Core\Repository\Values\Content\Content $foundContentInfo */ |
537
|
|
|
$this->assertEquals($publishedContent->id, $foundContentInfo->id); |
538
|
|
|
$this->assertEquals($newContentMetadataUpdateStruct->publishedDate, $foundContentInfo->publishedDate); |
|
|
|
|
539
|
|
|
$this->assertEquals($newLocation->id, $foundContentInfo->mainLocationId); |
|
|
|
|
540
|
|
|
$this->assertEquals($newContentMetadataUpdateStruct->remoteId, $foundContentInfo->remoteId); |
|
|
|
|
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
/** |
544
|
|
|
* Test that assigning section to content object properly affects Search Engine Index. |
545
|
|
|
*/ |
546
|
|
|
public function testAssignSection() |
547
|
|
|
{ |
548
|
|
|
$repository = $this->getRepository(); |
549
|
|
|
$sectionService = $repository->getSectionService(); |
550
|
|
|
$searchService = $repository->getSearchService(); |
551
|
|
|
|
552
|
|
|
$section = $sectionService->loadSection(2); |
553
|
|
|
$content = $this->createContentWithName('testAssignSection', [2]); |
554
|
|
|
|
555
|
|
|
$sectionService->assignSection($content->contentInfo, $section); |
556
|
|
|
$this->refreshSearch($repository); |
557
|
|
|
|
558
|
|
|
$criterion = new Criterion\ContentId($content->id); |
559
|
|
|
$query = new Query(['filter' => $criterion]); |
560
|
|
|
$results = $searchService->findContentInfo($query); |
561
|
|
|
$this->assertEquals($section->id, $results->searchHits[0]->valueObject->sectionId); |
|
|
|
|
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* Will create if not exists an simple content type for test purposes with just one required field name. |
566
|
|
|
* |
567
|
|
|
* @return \eZ\Publish\API\Repository\Values\ContentType\ContentType |
568
|
|
|
*/ |
569
|
|
|
protected function createTestContentType() |
570
|
|
|
{ |
571
|
|
|
$repository = $this->getRepository(); |
572
|
|
|
$contentTypeService = $repository->getContentTypeService(); |
573
|
|
|
$contentTypeIdentifier = 'test-type'; |
574
|
|
|
try { |
575
|
|
|
return $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); |
576
|
|
|
} catch (NotFoundException $e) { |
577
|
|
|
// continue creation process |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
$nameField = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); |
581
|
|
|
$nameField->fieldGroup = 'main'; |
582
|
|
|
$nameField->position = 1; |
583
|
|
|
$nameField->isTranslatable = true; |
584
|
|
|
$nameField->isSearchable = true; |
585
|
|
|
$nameField->isRequired = true; |
586
|
|
|
$contentTypeStruct = $contentTypeService->newContentTypeCreateStruct($contentTypeIdentifier); |
587
|
|
|
$contentTypeStruct->mainLanguageCode = 'eng-GB'; |
588
|
|
|
$contentTypeStruct->creatorId = 14; |
589
|
|
|
$contentTypeStruct->creationDate = new DateTime(); |
590
|
|
|
$contentTypeStruct->names = ['eng-GB' => 'Test Content Type']; |
591
|
|
|
$contentTypeStruct->addFieldDefinition($nameField); |
592
|
|
|
|
593
|
|
|
$contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); |
594
|
|
|
|
595
|
|
|
$contentTypeDraft = $contentTypeService->createContentType($contentTypeStruct, [$contentTypeGroup]); |
596
|
|
|
$contentTypeService->publishContentTypeDraft($contentTypeDraft); |
597
|
|
|
|
598
|
|
|
return $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
/** |
602
|
|
|
* Will create and publish an content with a filed with a given content name in location provided into |
603
|
|
|
* $parentLocationIdList. |
604
|
|
|
* |
605
|
|
|
* @param string $contentName |
606
|
|
|
* @param array $parentLocationIdList |
607
|
|
|
* |
608
|
|
|
* @return \eZ\Publish\API\Repository\Values\Content\Content |
609
|
|
|
*/ |
610
|
|
|
protected function createContentWithName($contentName, array $parentLocationIdList = array()) |
611
|
|
|
{ |
612
|
|
|
$contentService = $this->getRepository()->getContentService(); |
613
|
|
|
$locationService = $this->getRepository()->getLocationService(); |
614
|
|
|
|
615
|
|
|
$testableContentType = $this->createTestContentType(); |
616
|
|
|
|
617
|
|
|
$rootContentStruct = $contentService->newContentCreateStruct($testableContentType, 'eng-GB'); |
618
|
|
|
$rootContentStruct->setField('name', $contentName); |
619
|
|
|
|
620
|
|
|
$parentLocationList = []; |
621
|
|
|
foreach ($parentLocationIdList as $locationID) { |
622
|
|
|
$parentLocationList[] = $locationService->newLocationCreateStruct($locationID); |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
$contentDraft = $contentService->createContent($rootContentStruct, $parentLocationList); |
626
|
|
|
$publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); |
627
|
|
|
|
628
|
|
|
return $publishedContent; |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
/** |
632
|
|
|
* Asserts an content id if it exists still in the solr core. |
633
|
|
|
* |
634
|
|
|
* @param int $contentId |
635
|
|
|
* @param int $expectedCount |
636
|
|
|
*/ |
637
|
|
|
protected function assertContentIdSearch($contentId, $expectedCount) |
638
|
|
|
{ |
639
|
|
|
$searchService = $this->getRepository()->getSearchService(); |
640
|
|
|
|
641
|
|
|
$criterion = new Criterion\ContentId($contentId); |
642
|
|
|
$query = new Query(array('filter' => $criterion)); |
643
|
|
|
$result = $searchService->findContent($query); |
644
|
|
|
|
645
|
|
|
$this->assertEquals($expectedCount, $result->totalCount); |
646
|
|
|
if ($expectedCount == 0) { |
647
|
|
|
return; |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
$this->assertEquals( |
651
|
|
|
$contentId, |
652
|
|
|
$result->searchHits[0]->valueObject->id |
|
|
|
|
653
|
|
|
); |
654
|
|
|
} |
655
|
|
|
} |
656
|
|
|
|
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 thecomposer.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
orrequire-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 you have not tested against this specific condition, such errors might go unnoticed.