Completed
Push — content_service_typehint ( ae59a1...5fa235 )
by
unknown
38:00 queued 22:39
created

ContentTest::internalLoadContentProviderById()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File contains: eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Repository\Tests\Service\Mock;
10
11
use eZ\Publish\API\Repository\Repository;
12
use eZ\Publish\API\Repository\Values\Content\Language;
13
use eZ\Publish\API\Repository\Values\Content\Content as APIContent;
14
use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct;
15
use eZ\Publish\API\Repository\ContentTypeService as APIContentTypeService;
16
use eZ\Publish\API\Repository\LocationService as APILocationService;
17
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
18
use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo;
19
use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType;
20
use eZ\Publish\API\Repository\Values\Content\Location as APILocation;
21
use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition;
22
use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct;
23
use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException;
24
use eZ\Publish\Core\Base\Exceptions\ContentValidationException;
25
use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest;
26
use eZ\Publish\Core\Repository\ContentService;
27
use eZ\Publish\Core\Repository\Values\Content\Location;
28
use eZ\Publish\Core\Repository\Values\Content\Content;
29
use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct;
30
use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct;
31
use eZ\Publish\Core\Repository\Values\Content\VersionInfo;
32
use eZ\Publish\Core\Repository\Helper\DomainMapper;
33
use eZ\Publish\Core\Repository\Helper\RelationProcessor;
34
use eZ\Publish\Core\Repository\Helper\NameSchemaService;
35
use eZ\Publish\API\Repository\Values\Content\Field;
36
use eZ\Publish\Core\FieldType\Value;
37
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
38
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
39
use eZ\Publish\Core\Repository\Values\ContentType\ContentType;
40
use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition;
41
use eZ\Publish\SPI\Persistence\Content\Location as SPILocation;
42
use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType;
43
use eZ\Publish\SPI\Persistence\Content as SPIContent;
44
use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct;
45
use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct;
46
use eZ\Publish\SPI\Persistence\Content\Field as SPIField;
47
use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as SPIObjectStateGroup;
48
use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState;
49
use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo;
50
use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo;
51
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct;
52
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
53
use eZ\Publish\Core\Repository\Values\User\UserReference;
54
use Exception;
55
56
/**
57
 * Mock test case for Content service.
58
 */
59
class ContentTest extends BaseServiceMockTest
60
{
61
    /**
62
     * Represents empty Field Value.
63
     */
64
    const EMPTY_FIELD_VALUE = 'empty';
65
66
    /**
67
     * Test for the __construct() method.
68
     *
69
     * @covers \eZ\Publish\Core\Repository\ContentService::__construct
70
     */
71
    public function testConstructor(): void
72
    {
73
        $repositoryMock = $this->getRepositoryMock();
74
        /** @var \eZ\Publish\SPI\Persistence\Handler $persistenceHandlerMock */
75
        $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler');
76
        $domainMapperMock = $this->getDomainMapperMock();
77
        $relationProcessorMock = $this->getRelationProcessorMock();
78
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
79
        $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock();
80
        $permissionResolverMock = $this->getPermissionResolverMock();
81
        $settings = ['default_version_archive_limit' => 10];
82
83
        $service = new ContentService(
0 ignored issues
show
Unused Code introduced by
$service 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...
84
            $repositoryMock,
0 ignored issues
show
Bug introduced by
It seems like $repositoryMock defined by $this->getRepositoryMock() on line 73 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\API\Repository\Repository>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
85
            $persistenceHandlerMock,
86
            $domainMapperMock,
0 ignored issues
show
Bug introduced by
It seems like $domainMapperMock defined by $this->getDomainMapperMock() on line 76 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...ry\Helper\DomainMapper>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
87
            $relationProcessorMock,
0 ignored issues
show
Bug introduced by
It seems like $relationProcessorMock defined by $this->getRelationProcessorMock() on line 77 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...lper\RelationProcessor>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
88
            $nameSchemaServiceMock,
0 ignored issues
show
Bug introduced by
It seems like $nameSchemaServiceMock defined by $this->getNameSchemaServiceMock() on line 78 can also be of type object<PHPUnit\Framework\MockObject\MockObject>; however, eZ\Publish\Core\Reposito...tService::__construct() does only seem to accept object<eZ\Publish\Core\R...lper\NameSchemaService>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
89
            $fieldTypeRegistryMock,
90
            $permissionResolverMock,
91
            $settings
92
        );
93
    }
94
95
    /**
96
     * Test for the loadVersionInfo() method, of published version.
97
     *
98
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
99
     */
100
    public function testLoadVersionInfoById()
101
    {
102
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
103
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
104
        $contentHandler = $this->getPersistenceMock()->contentHandler();
105
        $domainMapperMock = $this->getDomainMapperMock();
106
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
107
        $permissionResolver = $this->getPermissionResolverMock();
108
109
        $versionInfoMock->expects($this->once())
110
            ->method('isPublished')
111
            ->willReturn(true);
112
113
        $contentServiceMock->expects($this->never())
114
            ->method('loadContentInfo');
115
116
        $contentHandler->expects($this->once())
117
            ->method('loadVersionInfo')
118
            ->with(
119
                $this->equalTo(42),
120
                $this->equalTo(null)
121
            )->will(
122
                $this->returnValue(new SPIVersionInfo())
123
            );
124
125
        $domainMapperMock->expects($this->once())
126
            ->method('buildVersionInfoDomainObject')
127
            ->with(new SPIVersionInfo())
128
            ->will($this->returnValue($versionInfoMock));
129
130
        $permissionResolver->expects($this->once())
131
            ->method('canUser')
132
            ->with(
133
                $this->equalTo('content'),
134
                $this->equalTo('read'),
135
                $this->equalTo($versionInfoMock)
136
            )->will($this->returnValue(true));
137
138
        $result = $contentServiceMock->loadVersionInfoById(42);
139
140
        $this->assertEquals($versionInfoMock, $result);
141
    }
142
143
    /**
144
     * Test for the loadVersionInfo() method, of a draft.
145
     *
146
     * @depends testLoadVersionInfoById
147
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
148
     */
149
    public function testLoadVersionInfoByIdAndVersionNumber()
150
    {
151
        $permissionResolver = $this->getPermissionResolverMock();
152
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
153
        /** @var \PHPUnit_Framework_MockObject_MockObject $contentHandler */
154
        $contentHandler = $this->getPersistenceMock()->contentHandler();
155
        $domainMapperMock = $this->getDomainMapperMock();
156
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
157
158
        $versionInfoMock->expects($this->any())
159
            ->method('__get')
160
            ->with('status')
161
            ->willReturn(APIVersionInfo::STATUS_DRAFT);
162
163
        $contentServiceMock->expects($this->never())
164
            ->method('loadContentInfo');
165
166
        $contentHandler->expects($this->once())
167
            ->method('loadVersionInfo')
168
            ->with(
169
                $this->equalTo(42),
170
                $this->equalTo(2)
171
            )->willReturn(new SPIVersionInfo());
172
173
        $domainMapperMock->expects($this->once())
174
            ->method('buildVersionInfoDomainObject')
175
            ->with(new SPIVersionInfo())
176
            ->willReturn($versionInfoMock);
177
178
        $permissionResolver->expects($this->once())
179
            ->method('canUser')
180
            ->with(
181
                $this->equalTo('content'),
182
                $this->equalTo('versionread'),
183
                $this->equalTo($versionInfoMock)
184
            )->willReturn(true);
185
186
        $result = $contentServiceMock->loadVersionInfoById(42, 2);
187
188
        $this->assertEquals($versionInfoMock, $result);
189
    }
190
191
    /**
192
     * Test for the loadVersionInfo() method.
193
     *
194
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
195
     */
196
    public function testLoadVersionInfoByIdThrowsNotFoundException()
197
    {
198
        $this->expectException(NotFoundException::class);
199
200
        $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']);
201
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
202
        $contentHandler = $this->getPersistenceMock()->contentHandler();
203
204
        $contentHandler->expects($this->once())
205
            ->method('loadVersionInfo')
206
            ->with(
207
                $this->equalTo(42),
208
                $this->equalTo(24)
209
            )->will(
210
                $this->throwException(
211
                    new NotFoundException(
212
                        'Content',
213
                        [
214
                            'contentId' => 42,
215
                            'versionNo' => 24,
216
                        ]
217
                    )
218
                )
219
            );
220
221
        $contentServiceMock->loadVersionInfoById(42, 24);
222
    }
223
224
    /**
225
     * Test for the loadVersionInfo() method.
226
     *
227
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
228
     */
229
    public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion()
230
    {
231
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
232
233
        $contentServiceMock = $this->getPartlyMockedContentService();
234
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
235
        $contentHandler = $this->getPersistenceMock()->contentHandler();
236
        $domainMapperMock = $this->getDomainMapperMock();
237
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
238
        $permissionResolver = $this->getPermissionResolverMock();
239
240
        $versionInfoMock->expects($this->any())
241
            ->method('isPublished')
242
            ->willReturn(false);
243
244
        $contentHandler->expects($this->once())
245
            ->method('loadVersionInfo')
246
            ->with(
247
                $this->equalTo(42),
248
                $this->equalTo(24)
249
            )->will(
250
                $this->returnValue(new SPIVersionInfo())
251
            );
252
253
        $domainMapperMock->expects($this->once())
254
            ->method('buildVersionInfoDomainObject')
255
            ->with(new SPIVersionInfo())
256
            ->will($this->returnValue($versionInfoMock));
257
258
        $permissionResolver->expects($this->once())
259
            ->method('canUser')
260
            ->with(
261
                $this->equalTo('content'),
262
                $this->equalTo('versionread'),
263
                $this->equalTo($versionInfoMock)
264
            )->will($this->returnValue(false));
265
266
        $contentServiceMock->loadVersionInfoById(42, 24);
267
    }
268
269
    /**
270
     * Test for the loadVersionInfo() method.
271
     *
272
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
273
     */
274
    public function testLoadVersionInfoByIdPublishedVersion()
275
    {
276
        $contentServiceMock = $this->getPartlyMockedContentService();
277
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
278
        $contentHandler = $this->getPersistenceMock()->contentHandler();
279
        $domainMapperMock = $this->getDomainMapperMock();
280
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
281
        $permissionResolver = $this->getPermissionResolverMock();
282
283
        $versionInfoMock->expects($this->once())
284
            ->method('isPublished')
285
            ->willReturn(true);
286
287
        $contentHandler->expects($this->once())
288
            ->method('loadVersionInfo')
289
            ->with(
290
                $this->equalTo(42),
291
                $this->equalTo(24)
292
            )->will(
293
                $this->returnValue(new SPIVersionInfo())
294
            );
295
296
        $domainMapperMock->expects($this->once())
297
            ->method('buildVersionInfoDomainObject')
298
            ->with(new SPIVersionInfo())
299
            ->will($this->returnValue($versionInfoMock));
300
301
        $permissionResolver->expects($this->once())
302
            ->method('canUser')
303
            ->with(
304
                $this->equalTo('content'),
305
                $this->equalTo('read'),
306
                $this->equalTo($versionInfoMock)
307
            )->will($this->returnValue(true));
308
309
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
310
311
        $this->assertEquals($versionInfoMock, $result);
312
    }
313
314
    /**
315
     * Test for the loadVersionInfo() method.
316
     *
317
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById
318
     */
319
    public function testLoadVersionInfoByIdNonPublishedVersion()
320
    {
321
        $contentServiceMock = $this->getPartlyMockedContentService();
322
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
323
        $contentHandler = $this->getPersistenceMock()->contentHandler();
324
        $domainMapperMock = $this->getDomainMapperMock();
325
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
326
        $permissionResolver = $this->getPermissionResolverMock();
327
328
        $versionInfoMock->expects($this->once())
329
            ->method('isPublished')
330
            ->willReturn(false);
331
332
        $contentHandler->expects($this->once())
333
            ->method('loadVersionInfo')
334
            ->with(
335
                $this->equalTo(42),
336
                $this->equalTo(24)
337
            )->will(
338
                $this->returnValue(new SPIVersionInfo())
339
            );
340
341
        $domainMapperMock->expects($this->once())
342
            ->method('buildVersionInfoDomainObject')
343
            ->with(new SPIVersionInfo())
344
            ->will($this->returnValue($versionInfoMock));
345
346
        $permissionResolver->expects($this->once())
347
            ->method('canUser')
348
            ->with(
349
                $this->equalTo('content'),
350
                $this->equalTo('versionread'),
351
                $this->equalTo($versionInfoMock)
352
            )->will($this->returnValue(true));
353
354
        $result = $contentServiceMock->loadVersionInfoById(42, 24);
355
356
        $this->assertEquals($versionInfoMock, $result);
357
    }
358
359
    /**
360
     * Test for the loadVersionInfo() method.
361
     *
362
     * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfo
363
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoById
364
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsNotFoundException
365
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion
366
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdPublishedVersion
367
     * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdNonPublishedVersion
368
     */
369
    public function testLoadVersionInfo()
370
    {
371
        $expectedResult = $this->createMock(VersionInfo::class);
372
373
        $contentServiceMock = $this->getPartlyMockedContentService(
374
            ['loadVersionInfoById']
375
        );
376
        $contentServiceMock->expects(
377
            $this->once()
378
        )->method(
379
            'loadVersionInfoById'
380
        )->with(
381
            $this->equalTo(42),
382
            $this->equalTo(7)
383
        )->will(
384
            $this->returnValue($expectedResult)
385
        );
386
387
        $result = $contentServiceMock->loadVersionInfo(
388
            new ContentInfo(['id' => 42]),
389
            7
390
        );
391
392
        $this->assertEquals($expectedResult, $result);
393
    }
394
395
    public function testLoadContent()
396
    {
397
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']);
398
        $content = $this->createMock(APIContent::class);
399
        $versionInfo = $this->createMock(APIVersionInfo::class);
400
        $permissionResolver = $this->getPermissionResolverMock();
401
402
        $content
403
            ->expects($this->once())
404
            ->method('getVersionInfo')
405
            ->will($this->returnValue($versionInfo));
406
        $versionInfo
407
            ->expects($this->once())
408
            ->method('isPublished')
409
            ->willReturn(true);
410
        $contentId = 123;
411
        $contentService
412
            ->expects($this->once())
413
            ->method('internalLoadContentById')
414
            ->with($contentId)
415
            ->will($this->returnValue($content));
416
417
        $permissionResolver
418
            ->expects($this->once())
419
            ->method('canUser')
420
            ->with('content', 'read', $content)
421
            ->will($this->returnValue(true));
422
423
        $this->assertSame($content, $contentService->loadContent($contentId));
424
    }
425
426
    public function testLoadContentNonPublished()
427
    {
428
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']);
429
        $content = $this->createMock(APIContent::class);
430
        $versionInfo = $this->createMock(APIVersionInfo::class);
431
        $permissionResolver = $this->getPermissionResolverMock();
432
433
        $content
434
            ->expects($this->once())
435
            ->method('getVersionInfo')
436
            ->will($this->returnValue($versionInfo));
437
        $contentId = 123;
438
        $contentService
439
            ->expects($this->once())
440
            ->method('internalLoadContentById')
441
            ->with($contentId)
442
            ->will($this->returnValue($content));
443
444
        $permissionResolver
445
            ->expects($this->exactly(2))
446
            ->method('canUser')
447
            ->will(
448
                $this->returnValueMap(
449
                    [
450
                        ['content', 'read', $content, [], true],
451
                        ['content', 'versionread', $content, [], true],
452
                    ]
453
                )
454
            );
455
456
        $this->assertSame($content, $contentService->loadContent($contentId));
457
    }
458
459
    public function testLoadContentUnauthorized()
460
    {
461
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
462
463
        $permissionResolver = $this->getPermissionResolverMock();
464
465
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']);
466
        $content = $this->createMock(APIContent::class);
467
        $contentId = 123;
468
        $contentService
469
            ->expects($this->once())
470
            ->method('internalLoadContentById')
471
            ->with($contentId)
472
            ->will($this->returnValue($content));
473
474
        $permissionResolver
475
            ->expects($this->once())
476
            ->method('canUser')
477
            ->with('content', 'read', $content)
478
            ->will($this->returnValue(false));
479
480
        $contentService->loadContent($contentId);
481
    }
482
483
    public function testLoadContentNotPublishedStatusUnauthorized()
484
    {
485
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
486
487
        $permissionResolver = $this->getPermissionResolverMock();
488
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']);
489
        $content = $this->createMock(APIContent::class);
490
        $versionInfo = $this
491
            ->getMockBuilder(APIVersionInfo::class)
492
            ->getMockForAbstractClass();
493
        $content
494
            ->expects($this->once())
495
            ->method('getVersionInfo')
496
            ->will($this->returnValue($versionInfo));
497
        $contentId = 123;
498
        $contentService
499
            ->expects($this->once())
500
            ->method('internalLoadContentById')
501
            ->with($contentId)
502
            ->will($this->returnValue($content));
503
504
        $permissionResolver
505
            ->expects($this->exactly(2))
506
            ->method('canUser')
507
            ->will(
508
                $this->returnValueMap(
509
                    [
510
                        ['content', 'read', $content, [], true],
511
                        ['content', 'versionread', $content, [], false],
512
                    ]
513
                )
514
            );
515
516
        $contentService->loadContent($contentId);
517
    }
518
519
    /**
520
     * @dataProvider internalLoadContentProviderById
521
     */
522
    public function testInternalLoadContentById(int $id, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable): void
523
    {
524
        if (!empty($languages) && $useAlwaysAvailable) {
525
            $spiContentInfo = new SPIContentInfo(['id' => $id, 'alwaysAvailable' => false]);
526
        } else {
527
            $spiContentInfo = new SPIContentInfo(['id' => $id]);
528
        }
529
530
        $spiContent = new SPIContent([
531
            'versionInfo' => new VersionInfo([
532
                'contentInfo' => new ContentInfo([
533
                    'id' => 42,
534
                    'contentTypeId' => 123,
535
                ]),
536
            ]),
537
        ]);
538
539
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
540
        $contentHandler = $this->getPersistenceMock()->contentHandler();
541
        $contentHandler
542
            ->expects($this->once())
543
            ->method('loadContentInfo')
544
            ->with($id)
545
            ->will($this->returnValue($spiContentInfo));
546
547
        $contentHandler
548
            ->expects($this->once())
549
            ->method('load')
550
            ->with($id, $versionNo, $languages)
551
            ->willReturn($spiContent);
552
553
        $contentService = $this->getPartlyMockedContentService();
554
555
        $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages);
556
        $actualContent = $contentService->internalLoadContentById($id, $languages, $versionNo, $useAlwaysAvailable);
557
558
        $this->assertSame($expectedContent, $actualContent);
559
    }
560
561
    /**
562
     * @dataProvider internalLoadContentProviderByRemoteId
563
     */
564
    public function testInternalLoadContentByRemoteId(string $remoteId, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable)
565
    {
566
        $realId = 123;
567
568
        $spiContentInfo = new SPIContentInfo([
569
            'currentVersionNo' => $versionNo ?: 7,
570
            'id' => $realId,
571
        ]);
572
573
        $spiContent = new SPIContent([
574
            'versionInfo' => new VersionInfo([
575
                'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
576
            ]),
577
        ]);
578
579
        $contentService = $this->getPartlyMockedContentService();
580
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
581
        $contentHandler = $this->getPersistenceMock()->contentHandler();
582
        $contentHandler
583
            ->expects($this->once())
584
            ->method('loadContentInfoByRemoteId')
585
            ->with($remoteId)
586
            ->will($this->returnValue($spiContentInfo));
587
588
        $contentHandler
589
            ->expects($this->once())
590
            ->method('load')
591
            ->with($realId, $versionNo, $languages)
592
            ->willReturn($spiContent);
593
594
        $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages);
595
596
        $actualContent = $contentService->internalLoadContentByRemoteId(
597
            $remoteId,
598
            $languages,
599
            $versionNo,
600
            $useAlwaysAvailable
601
        );
602
603
        $this->assertSame($expectedContent, $actualContent);
604
    }
605
606
    public function internalLoadContentProviderById(): array
607
    {
608
        return [
609
            [123, null, null, false],
610
            [123, null, 456, false],
611
            [456, null, 123, true],
612
            [456, null, 2, false],
613
            [456, ['eng-GB'], 2, true],
614
            [456, ['eng-GB', 'fre-FR'], null, false],
615
            [456, ['eng-GB', 'fre-FR', 'nor-NO'], 2, false],
616
        ];
617
    }
618
619
    public function internalLoadContentProviderByRemoteId(): array
620
    {
621
        return [
622
            ['123', null, null, false],
623
            ['someRemoteId', null, 456, false],
624
            ['456', null, 123, false],
625
            ['someRemoteId', null, 2, false],
626
            ['someRemoteId', ['eng-GB'], 2, false],
627
            ['456', ['eng-GB', 'fre-FR'], null, false],
628
            ['someRemoteId', ['eng-GB', 'fre-FR', 'nor-NO'], 2, false],
629
        ];
630
    }
631
632
    public function testInternalLoadContentByIdNotFound(): void
633
    {
634
        $this->expectException(NotFoundException::class);
635
636
        $id = 123;
637
        $versionNo = 7;
638
        $languages = null;
639
640
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
641
        $contentHandler = $this->getPersistenceMock()->contentHandler();
642
        $contentHandler
643
            ->expects($this->once())
644
            ->method('loadContentInfo')
645
            ->with($id)
646
            ->willReturn(new SPIContent\ContentInfo(['id' => $id]));
647
648
        $contentHandler
649
            ->expects($this->once())
650
            ->method('load')
651
            ->with($id, $versionNo, $languages)
652
            ->will(
653
                $this->throwException(
654
                    $this->createMock(APINotFoundException::class)
655
                )
656
            );
657
658
        $contentService = $this->getPartlyMockedContentService();
659
        $contentService->internalLoadContentById($id, $languages, $versionNo);
660
    }
661
662
    public function testInternalLoadContentByRemoteIdNotFound(): void
663
    {
664
        $this->expectException(NotFoundException::class);
665
666
        $remoteId = 'dca290623518d393126d3408b45af6ee';
667
        $id = 123;
668
        $versionNo = 7;
669
        $languages = null;
670
671
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
672
        $contentHandler = $this->getPersistenceMock()->contentHandler();
673
        $contentHandler
674
            ->expects($this->once())
675
            ->method('loadContentInfoByRemoteId')
676
            ->with($remoteId)
677
            ->willReturn(new SPIContent\ContentInfo(['id' => $id]));
678
679
        $contentHandler
680
            ->expects($this->once())
681
            ->method('load')
682
            ->with($id, $versionNo, $languages)
683
            ->willThrowException(
684
                $this->createMock(APINotFoundException::class)
685
            );
686
687
        $contentService = $this->getPartlyMockedContentService();
688
        $contentService->internalLoadContentByRemoteId($remoteId, $languages, $versionNo);
689
    }
690
691
    /**
692
     * Test for the loadContentByContentInfo() method.
693
     *
694
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByContentInfo
695
     */
696
    public function testLoadContentByContentInfo()
697
    {
698
        $expectedResult = $this->createMock(Content::class);
699
700
        $contentServiceMock = $this->getPartlyMockedContentService(
701
            ['loadContent']
702
        );
703
        $contentServiceMock->expects(
704
            $this->once()
705
        )->method(
706
            'loadContent'
707
        )->with(
708
            $this->equalTo(42),
709
            $this->equalTo(['cro-HR']),
710
            $this->equalTo(7),
711
            $this->equalTo(false)
712
        )->will(
713
            $this->returnValue($expectedResult)
714
        );
715
716
        $result = $contentServiceMock->loadContentByContentInfo(
717
            new ContentInfo(['id' => 42]),
718
            ['cro-HR'],
719
            7
720
        );
721
722
        $this->assertEquals($expectedResult, $result);
723
    }
724
725
    /**
726
     * Test for the loadContentByVersionInfo() method.
727
     *
728
     * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByVersionInfo
729
     */
730
    public function testLoadContentByVersionInfo()
731
    {
732
        $expectedResult = $this->createMock(Content::class);
733
734
        $contentServiceMock = $this->getPartlyMockedContentService(
735
            ['loadContent']
736
        );
737
        $contentServiceMock->expects(
738
            $this->once()
739
        )->method(
740
            'loadContent'
741
        )->with(
742
            $this->equalTo(42),
743
            $this->equalTo(['cro-HR']),
744
            $this->equalTo(7),
745
            $this->equalTo(false)
746
        )->will(
747
            $this->returnValue($expectedResult)
748
        );
749
750
        $result = $contentServiceMock->loadContentByVersionInfo(
751
            new VersionInfo(
752
                [
753
                    'contentInfo' => new ContentInfo(['id' => 42]),
754
                    'versionNo' => 7,
755
                ]
756
            ),
757
            ['cro-HR']
758
        );
759
760
        $this->assertEquals($expectedResult, $result);
761
    }
762
763
    /**
764
     * Test for the deleteContent() method.
765
     *
766
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
767
     */
768
    public function testDeleteContentThrowsUnauthorizedException()
769
    {
770
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
771
772
        $permissionResolver = $this->getPermissionResolverMock();
773
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']);
774
        $contentInfo = $this->createMock(APIContentInfo::class);
775
776
        $contentInfo->expects($this->any())
777
            ->method('__get')
778
            ->with('id')
779
            ->will($this->returnValue(42));
780
781
        $contentService->expects($this->once())
782
            ->method('internalLoadContentInfoById')
783
            ->with(42)
784
            ->will($this->returnValue($contentInfo));
785
786
        $permissionResolver->expects($this->once())
787
            ->method('canUser')
788
            ->with('content', 'remove')
789
            ->will($this->returnValue(false));
790
791
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
792
        $contentService->deleteContent($contentInfo);
793
    }
794
795
    /**
796
     * Test for the deleteContent() method.
797
     *
798
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
799
     */
800
    public function testDeleteContent()
801
    {
802
        $repository = $this->getRepositoryMock();
803
        $permissionResolver = $this->getPermissionResolverMock();
804
805
        $permissionResolver->expects($this->once())
806
            ->method('canUser')
807
            ->with('content', 'remove')
808
            ->will($this->returnValue(true));
809
810
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']);
811
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandler */
812
        $urlAliasHandler = $this->getPersistenceMock()->urlAliasHandler();
813
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
814
        $locationHandler = $this->getPersistenceMock()->locationHandler();
815
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
816
        $contentHandler = $this->getPersistenceMock()->contentHandler();
817
818
        $contentInfo = $this->createMock(APIContentInfo::class);
819
820
        $contentService->expects($this->once())
821
            ->method('internalLoadContentInfoById')
822
            ->with(42)
823
            ->will($this->returnValue($contentInfo));
824
825
        $contentInfo->expects($this->any())
826
            ->method('__get')
827
            ->with('id')
828
            ->will($this->returnValue(42));
829
830
        $repository->expects($this->once())->method('beginTransaction');
831
832
        $spiLocations = [
833
            new SPILocation(['id' => 1]),
834
            new SPILocation(['id' => 2]),
835
        ];
836
        $locationHandler->expects($this->once())
837
            ->method('loadLocationsByContent')
838
            ->with(42)
839
            ->will($this->returnValue($spiLocations));
840
841
        $contentHandler->expects($this->once())
842
            ->method('deleteContent')
843
            ->with(42);
844
845
        foreach ($spiLocations as $index => $spiLocation) {
846
            $urlAliasHandler->expects($this->at($index))
847
                ->method('locationDeleted')
848
                ->with($spiLocation->id);
849
        }
850
851
        $repository->expects($this->once())->method('commit');
852
853
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
854
        $contentService->deleteContent($contentInfo);
855
    }
856
857
    /**
858
     * Test for the deleteContent() method.
859
     *
860
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent
861
     */
862
    public function testDeleteContentWithRollback()
863
    {
864
        $this->expectException(\Exception::class);
865
866
        $repository = $this->getRepositoryMock();
867
        $permissionResolver = $this->getPermissionResolverMock();
868
869
        $permissionResolver->expects($this->once())
870
            ->method('canUser')
871
            ->with('content', 'remove')
872
            ->will($this->returnValue(true));
873
874
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']);
875
        /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */
876
        $locationHandler = $this->getPersistenceMock()->locationHandler();
877
878
        $contentInfo = $this->createMock(APIContentInfo::class);
879
880
        $contentService->expects($this->once())
881
            ->method('internalLoadContentInfoById')
882
            ->with(42)
883
            ->will($this->returnValue($contentInfo));
884
885
        $contentInfo->expects($this->any())
886
            ->method('__get')
887
            ->with('id')
888
            ->will($this->returnValue(42));
889
890
        $repository->expects($this->once())->method('beginTransaction');
891
892
        $locationHandler->expects($this->once())
893
            ->method('loadLocationsByContent')
894
            ->with(42)
895
            ->will($this->throwException(new \Exception()));
896
897
        $repository->expects($this->once())->method('rollback');
898
899
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
900
        $contentService->deleteContent($contentInfo);
901
    }
902
903
    /**
904
     * Test for the deleteVersion() method.
905
     *
906
     * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion
907
     */
908
    public function testDeleteVersionThrowsBadStateExceptionLastVersion()
909
    {
910
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
911
912
        $repository = $this->getRepositoryMock();
913
        $permissionResolver = $this->getPermissionResolverMock();
914
915
        $permissionResolver
916
            ->expects($this->once())
917
            ->method('canUser')
918
            ->with('content', 'versionremove')
919
            ->will($this->returnValue(true));
920
        $repository
921
            ->expects($this->never())
922
            ->method('beginTransaction');
923
924
        $contentService = $this->getPartlyMockedContentService();
925
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */
926
        $contentHandler = $this->getPersistenceMock()->contentHandler();
927
        $contentInfo = $this->createMock(APIContentInfo::class);
928
        $versionInfo = $this->createMock(APIVersionInfo::class);
929
930
        $contentInfo
931
            ->expects($this->any())
932
            ->method('__get')
933
            ->with('id')
934
            ->will($this->returnValue(42));
935
936
        $versionInfo
937
            ->expects($this->any())
938
            ->method('__get')
939
            ->will(
940
                $this->returnValueMap(
941
                    [
942
                        ['versionNo', 123],
943
                        ['contentInfo', $contentInfo],
944
                    ]
945
                )
946
            );
947
        $versionInfo
948
            ->expects($this->once())
949
            ->method('isPublished')
950
            ->willReturn(false);
951
952
        $contentHandler
953
            ->expects($this->once())
954
            ->method('listVersions')
955
            ->with(42)
956
            ->will($this->returnValue(['version']));
957
958
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo */
959
        $contentService->deleteVersion($versionInfo);
960
    }
961
962
    /**
963
     * Test for the createContent() method.
964
     *
965
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
966
     */
967
    public function testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet()
968
    {
969
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
970
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'mainLanguageCode\' property must be set');
971
972
        $mockedService = $this->getPartlyMockedContentService();
973
        $mockedService->createContent(new ContentCreateStruct(), []);
974
    }
975
976
    /**
977
     * Test for the createContent() method.
978
     *
979
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
980
     */
981
    public function testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet()
982
    {
983
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
984
        $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: \'contentType\' property must be set');
985
986
        $mockedService = $this->getPartlyMockedContentService();
987
        $mockedService->createContent(
988
            new ContentCreateStruct(['mainLanguageCode' => 'eng-US']),
989
            []
990
        );
991
    }
992
993
    /**
994
     * Test for the createContent() method.
995
     *
996
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
997
     */
998
    public function testCreateContentThrowsUnauthorizedException()
999
    {
1000
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
1001
1002
        $repositoryMock = $this->getRepositoryMock();
1003
1004
        $permissionResolver = $this->getPermissionResolverMock();
1005
        $permissionResolver->expects($this->once())
1006
            ->method('getCurrentUserReference')
1007
            ->will($this->returnValue(new UserReference(169)));
1008
1009
        $mockedService = $this->getPartlyMockedContentService();
1010
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1011
        $contentType = new ContentType(
1012
            [
1013
                'id' => 123,
1014
                'fieldDefinitions' => [],
1015
            ]
1016
        );
1017
        $contentCreateStruct = new ContentCreateStruct(
1018
            [
1019
                'ownerId' => 169,
1020
                'alwaysAvailable' => false,
1021
                'mainLanguageCode' => 'eng-US',
1022
                'contentType' => $contentType,
1023
            ]
1024
        );
1025
1026
        $contentTypeServiceMock->expects($this->once())
1027
            ->method('loadContentType')
1028
            ->with($this->equalTo(123))
1029
            ->will($this->returnValue($contentType));
1030
1031
        $repositoryMock->expects($this->once())
1032
            ->method('getContentTypeService')
1033
            ->will($this->returnValue($contentTypeServiceMock));
1034
1035
        $permissionResolver->expects($this->once())
1036
            ->method('canUser')
1037
            ->with(
1038
                $this->equalTo('content'),
1039
                $this->equalTo('create'),
1040
                $this->isInstanceOf(get_class($contentCreateStruct)),
1041
                $this->equalTo([])
1042
            )->will($this->returnValue(false));
1043
1044
        $mockedService->createContent(
1045
            new ContentCreateStruct(
1046
                [
1047
                    'mainLanguageCode' => 'eng-US',
1048
                    'contentType' => $contentType,
1049
                ]
1050
            ),
1051
            []
1052
        );
1053
    }
1054
1055
    /**
1056
     * Test for the createContent() method.
1057
     *
1058
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1059
     * @exceptionMessage Argument '$contentCreateStruct' is invalid: Another content with remoteId 'faraday' exists
1060
     */
1061
    public function testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId()
1062
    {
1063
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1064
1065
        $repositoryMock = $this->getRepositoryMock();
1066
        $permissionResolverMock = $this->getPermissionResolverMock();
1067
        $permissionResolverMock
1068
            ->expects($this->once())
1069
            ->method('getCurrentUserReference')
1070
            ->willReturn($this->createMock(UserReference::class));
1071
1072
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
1073
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1074
        $contentType = new ContentType(
1075
            [
1076
                'id' => 123,
1077
                'fieldDefinitions' => [],
1078
            ]
1079
        );
1080
        $contentCreateStruct = new ContentCreateStruct(
1081
            [
1082
                'ownerId' => 169,
1083
                'alwaysAvailable' => false,
1084
                'remoteId' => 'faraday',
1085
                'mainLanguageCode' => 'eng-US',
1086
                'contentType' => $contentType,
1087
            ]
1088
        );
1089
1090
        $contentTypeServiceMock->expects($this->once())
1091
            ->method('loadContentType')
1092
            ->with($this->equalTo(123))
1093
            ->will($this->returnValue($contentType));
1094
1095
        $repositoryMock->expects($this->once())
1096
            ->method('getContentTypeService')
1097
            ->will($this->returnValue($contentTypeServiceMock));
1098
1099
        $permissionResolverMock->expects($this->once())
1100
            ->method('canUser')
1101
            ->with(
1102
                $this->equalTo('content'),
1103
                $this->equalTo('create'),
1104
                $this->isInstanceOf(get_class($contentCreateStruct)),
1105
                $this->equalTo([])
1106
            )->will($this->returnValue(true));
1107
1108
        $mockedService->expects($this->once())
1109
            ->method('loadContentByRemoteId')
1110
            ->with($contentCreateStruct->remoteId)
1111
            ->will($this->returnValue($this->createMock(Content::class)));
1112
1113
        $mockedService->createContent(
1114
            new ContentCreateStruct(
1115
                [
1116
                    'remoteId' => 'faraday',
1117
                    'mainLanguageCode' => 'eng-US',
1118
                    'contentType' => $contentType,
1119
                ]
1120
            ),
1121
            []
1122
        );
1123
    }
1124
1125
    /**
1126
     * @param string $mainLanguageCode
1127
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1128
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1129
     *
1130
     * @return array
1131
     */
1132
    protected function mapStructFieldsForCreate($mainLanguageCode, $structFields, $fieldDefinitions)
1133
    {
1134
        $mappedFieldDefinitions = [];
1135
        foreach ($fieldDefinitions as $fieldDefinition) {
1136
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
1137
        }
1138
1139
        $mappedStructFields = [];
1140
        foreach ($structFields as $structField) {
1141
            if ($structField->languageCode === null) {
1142
                $languageCode = $mainLanguageCode;
1143
            } else {
1144
                $languageCode = $structField->languageCode;
1145
            }
1146
1147
            $mappedStructFields[$structField->fieldDefIdentifier][$languageCode] = (string)$structField->value;
1148
        }
1149
1150
        return $mappedStructFields;
1151
    }
1152
1153
    /**
1154
     * Returns full, possibly redundant array of field values, indexed by field definition
1155
     * identifier and language code.
1156
     *
1157
     * @throws \RuntimeException Method is intended to be used only with consistent fixtures
1158
     *
1159
     * @param string $mainLanguageCode
1160
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1161
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1162
     * @param array $languageCodes
1163
     *
1164
     * @return array
1165
     */
1166
    protected function determineValuesForCreate(
1167
        $mainLanguageCode,
1168
        array $structFields,
1169
        array $fieldDefinitions,
1170
        array $languageCodes
1171
    ) {
1172
        $mappedStructFields = $this->mapStructFieldsForCreate(
1173
            $mainLanguageCode,
1174
            $structFields,
1175
            $fieldDefinitions
1176
        );
1177
1178
        $values = [];
1179
1180
        foreach ($fieldDefinitions as $fieldDefinition) {
1181
            $identifier = $fieldDefinition->identifier;
1182
            foreach ($languageCodes as $languageCode) {
1183
                if (!$fieldDefinition->isTranslatable) {
1184
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
1185
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
1186
                    } else {
1187
                        $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1188
                    }
1189
                    continue;
1190
                }
1191
1192
                if (isset($mappedStructFields[$identifier][$languageCode])) {
1193
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
1194
                    continue;
1195
                }
1196
1197
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1198
            }
1199
        }
1200
1201
        return $this->stubValues($values);
1202
    }
1203
1204
    /**
1205
     * @param string $mainLanguageCode
1206
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1207
     *
1208
     * @return string[]
1209
     */
1210
    protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields)
1211
    {
1212
        $languageCodes = [];
1213
1214
        foreach ($structFields as $field) {
1215
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1216
                continue;
1217
            }
1218
1219
            $languageCodes[$field->languageCode] = true;
1220
        }
1221
1222
        $languageCodes[$mainLanguageCode] = true;
1223
1224
        return array_keys($languageCodes);
1225
    }
1226
1227
    /**
1228
     * Asserts that calling createContent() with given API field set causes calling
1229
     * Handler::createContent() with given SPI field set.
1230
     *
1231
     * @param string $mainLanguageCode
1232
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1233
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
1234
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1235
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
1236
     * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] $objectStateGroups
0 ignored issues
show
Bug introduced by
There is no parameter named $objectStateGroups. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1237
     * @param bool $execute
1238
     *
1239
     * @return mixed
1240
     */
1241
    protected function assertForTestCreateContentNonRedundantFieldSet(
1242
        $mainLanguageCode,
1243
        array $structFields,
1244
        array $spiFields,
1245
        array $fieldDefinitions,
1246
        array $locationCreateStructs = [],
1247
        $withObjectStates = false,
1248
        $execute = true
1249
    ) {
1250
        $repositoryMock = $this->getRepositoryMock();
1251
        $mockedService = $this->getPartlyMockedContentService();
1252
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
1253
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
1254
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1255
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1256
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
1257
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
1258
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1259
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock 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...
1260
        $domainMapperMock = $this->getDomainMapperMock();
1261
        $relationProcessorMock = $this->getRelationProcessorMock();
1262
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
1263
        $permissionResolverMock = $this->getPermissionResolverMock();
1264
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
1265
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
1266
        $contentType = new ContentType(
1267
            [
1268
                'id' => 123,
1269
                'fieldDefinitions' => $fieldDefinitions,
1270
                'nameSchema' => '<nameSchema>',
1271
            ]
1272
        );
1273
        $contentCreateStruct = new ContentCreateStruct(
1274
            [
1275
                'fields' => $structFields,
1276
                'mainLanguageCode' => $mainLanguageCode,
1277
                'contentType' => $contentType,
1278
                'alwaysAvailable' => false,
1279
                'ownerId' => 169,
1280
                'sectionId' => 1,
1281
            ]
1282
        );
1283
1284
        $languageHandlerMock->expects($this->any())
1285
            ->method('loadByLanguageCode')
1286
            ->with($this->isType('string'))
1287
            ->will(
1288
                $this->returnCallback(
1289
                    function () {
1290
                        return new Language(['id' => 4242]);
1291
                    }
1292
                )
1293
            );
1294
1295
        $repositoryMock->expects($this->once())->method('beginTransaction');
1296
1297
        $contentTypeServiceMock->expects($this->once())
1298
            ->method('loadContentType')
1299
            ->with($this->equalTo($contentType->id))
1300
            ->will($this->returnValue($contentType));
1301
1302
        $repositoryMock->expects($this->once())
1303
            ->method('getContentTypeService')
1304
            ->will($this->returnValue($contentTypeServiceMock));
1305
1306
        $that = $this;
1307
        $permissionResolverMock->expects($this->once())
1308
            ->method('canUser')
1309
            ->with(
1310
                $this->equalTo('content'),
1311
                $this->equalTo('create'),
1312
                $this->isInstanceOf(APIContentCreateStruct::class),
1313
                $this->equalTo($locationCreateStructs)
1314
            )->will(
1315
                $this->returnCallback(
1316
                    function () use ($that, $contentCreateStruct) {
1317
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1318
1319
                        return true;
1320
                    }
1321
                )
1322
            );
1323
1324
        $domainMapperMock->expects($this->once())
1325
            ->method('getUniqueHash')
1326
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1327
            ->will(
1328
                $this->returnCallback(
1329
                    function ($object) use ($that, $contentCreateStruct) {
1330
                        $that->assertEquals($contentCreateStruct, $object);
1331
1332
                        return 'hash';
1333
                    }
1334
                )
1335
            );
1336
1337
        $fieldTypeMock->expects($this->any())
1338
            ->method('acceptValue')
1339
            ->will(
1340
                $this->returnCallback(
1341
                    function ($valueString) {
1342
                        return new ValueStub($valueString);
1343
                    }
1344
                )
1345
            );
1346
1347
        $fieldTypeMock->expects($this->any())
1348
            ->method('toPersistenceValue')
1349
            ->will(
1350
                $this->returnCallback(
1351
                    function (ValueStub $value) {
1352
                        return (string)$value;
1353
                    }
1354
                )
1355
            );
1356
1357
        $emptyValue = self::EMPTY_FIELD_VALUE;
1358
        $fieldTypeMock->expects($this->any())
1359
            ->method('isEmptyValue')
1360
            ->will(
1361
                $this->returnCallback(
1362
                    function (ValueStub $value) use ($emptyValue) {
1363
                        return $emptyValue === (string)$value;
1364
                    }
1365
                )
1366
            );
1367
1368
        $fieldTypeMock->expects($this->any())
1369
            ->method('validate')
1370
            ->will($this->returnValue([]));
1371
1372
        $this->getFieldTypeRegistryMock()->expects($this->any())
1373
            ->method('getFieldType')
1374
            ->will($this->returnValue($fieldTypeMock));
1375
1376
        $relationProcessorMock
1377
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
1378
            ->method('appendFieldRelations')
1379
            ->with(
1380
                $this->isType('array'),
1381
                $this->isType('array'),
1382
                $this->isInstanceOf(SPIFieldType::class),
1383
                $this->isInstanceOf(Value::class),
1384
                $this->anything()
1385
            );
1386
1387
        $values = $this->determineValuesForCreate(
1388
            $mainLanguageCode,
1389
            $structFields,
1390
            $fieldDefinitions,
1391
            $languageCodes
1392
        );
1393
        $nameSchemaServiceMock->expects($this->once())
1394
            ->method('resolve')
1395
            ->with(
1396
                $this->equalTo($contentType->nameSchema),
1397
                $this->equalTo($contentType),
1398
                $this->equalTo($values),
1399
                $this->equalTo($languageCodes)
1400
            )->will($this->returnValue([]));
1401
1402
        $relationProcessorMock->expects($this->any())
1403
            ->method('processFieldRelations')
1404
            ->with(
1405
                $this->isType('array'),
1406
                $this->equalTo(42),
1407
                $this->isType('int'),
1408
                $this->equalTo($contentType),
1409
                $this->equalTo([])
1410
            );
1411
1412
        if (!$withObjectStates) {
1413
            $objectStateHandlerMock->expects($this->once())
1414
                ->method('loadAllGroups')
1415
                ->will($this->returnValue([]));
1416
        }
1417
1418
        if ($execute) {
1419
            $spiContentCreateStruct = new SPIContentCreateStruct(
1420
                [
1421
                    'name' => [],
1422
                    'typeId' => 123,
1423
                    'sectionId' => 1,
1424
                    'ownerId' => 169,
1425
                    'remoteId' => 'hash',
1426
                    'fields' => $spiFields,
1427
                    'modified' => time(),
1428
                    'initialLanguageId' => 4242,
1429
                ]
1430
            );
1431
            $spiContentCreateStruct2 = clone $spiContentCreateStruct;
1432
            ++$spiContentCreateStruct2->modified;
1433
1434
            $spiContent = new SPIContent(
1435
                [
1436
                    'versionInfo' => new SPIContent\VersionInfo(
1437
                        [
1438
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
1439
                            'versionNo' => 7,
1440
                        ]
1441
                    ),
1442
                ]
1443
            );
1444
1445
            $contentHandlerMock->expects($this->once())
1446
                ->method('create')
1447
                ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
1448
                ->will($this->returnValue($spiContent));
1449
1450
            $repositoryMock->expects($this->once())->method('commit');
1451
            $domainMapperMock->expects($this->once())
1452
                ->method('buildContentDomainObject')
1453
                ->with(
1454
                    $this->isInstanceOf(SPIContent::class),
1455
                    $this->equalTo($contentType)
1456
                )
1457
                ->willReturn($this->createMock(APIContent::class));
1458
1459
            $mockedService->createContent($contentCreateStruct, []);
1460
        }
1461
1462
        return $contentCreateStruct;
1463
    }
1464
1465
    public function providerForTestCreateContentNonRedundantFieldSet1()
1466
    {
1467
        $spiFields = [
1468
            new SPIField(
1469
                [
1470
                    'fieldDefinitionId' => 'fieldDefinitionId',
1471
                    'type' => 'fieldTypeIdentifier',
1472
                    'value' => 'newValue',
1473
                    'languageCode' => 'eng-US',
1474
                ]
1475
            ),
1476
        ];
1477
1478
        return [
1479
            // 0. Without language set
1480
            [
1481
                'eng-US',
1482
                [
1483
                    new Field(
1484
                        [
1485
                            'fieldDefIdentifier' => 'identifier',
1486
                            'value' => 'newValue',
1487
                            'languageCode' => 'eng-US',
1488
                        ]
1489
                    ),
1490
                ],
1491
                $spiFields,
1492
            ],
1493
            // 1. Without language set
1494
            [
1495
                'eng-US',
1496
                [
1497
                    new Field(
1498
                        [
1499
                            'fieldDefIdentifier' => 'identifier',
1500
                            'value' => 'newValue',
1501
                            'languageCode' => null,
1502
                        ]
1503
                    ),
1504
                ],
1505
                $spiFields,
1506
            ],
1507
        ];
1508
    }
1509
1510
    /**
1511
     * Test for the createContent() method.
1512
     *
1513
     * Testing the simplest use case.
1514
     *
1515
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1516
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1517
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1518
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1519
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1520
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet1
1521
     */
1522
    public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields)
1523
    {
1524
        $fieldDefinitions = [
1525
            new FieldDefinition(
1526
                [
1527
                    'id' => 'fieldDefinitionId',
1528
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1529
                    'isTranslatable' => false,
1530
                    'identifier' => 'identifier',
1531
                    'isRequired' => false,
1532
                    'defaultValue' => 'defaultValue',
1533
                ]
1534
            ),
1535
        ];
1536
1537
        $this->assertForTestCreateContentNonRedundantFieldSet(
1538
            $mainLanguageCode,
1539
            $structFields,
1540
            $spiFields,
1541
            $fieldDefinitions
1542
        );
1543
    }
1544
1545
    public function providerForTestCreateContentNonRedundantFieldSet2()
1546
    {
1547
        $spiFields = [
1548
            new SPIField(
1549
                [
1550
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1551
                    'type' => 'fieldTypeIdentifier',
1552
                    'value' => 'newValue1',
1553
                    'languageCode' => 'eng-US',
1554
                ]
1555
            ),
1556
            new SPIField(
1557
                [
1558
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1559
                    'type' => 'fieldTypeIdentifier',
1560
                    'value' => 'newValue2',
1561
                    'languageCode' => 'ger-DE',
1562
                ]
1563
            ),
1564
        ];
1565
1566
        return [
1567
            // 0. With language set
1568
            [
1569
                'eng-US',
1570
                [
1571
                    new Field(
1572
                        [
1573
                            'fieldDefIdentifier' => 'identifier1',
1574
                            'value' => 'newValue1',
1575
                            'languageCode' => 'eng-US',
1576
                        ]
1577
                    ),
1578
                    new Field(
1579
                        [
1580
                            'fieldDefIdentifier' => 'identifier2',
1581
                            'value' => 'newValue2',
1582
                            'languageCode' => 'ger-DE',
1583
                        ]
1584
                    ),
1585
                ],
1586
                $spiFields,
1587
            ],
1588
            // 1. Without language set
1589
            [
1590
                'eng-US',
1591
                [
1592
                    new Field(
1593
                        [
1594
                            'fieldDefIdentifier' => 'identifier1',
1595
                            'value' => 'newValue1',
1596
                            'languageCode' => null,
1597
                        ]
1598
                    ),
1599
                    new Field(
1600
                        [
1601
                            'fieldDefIdentifier' => 'identifier2',
1602
                            'value' => 'newValue2',
1603
                            'languageCode' => 'ger-DE',
1604
                        ]
1605
                    ),
1606
                ],
1607
                $spiFields,
1608
            ],
1609
        ];
1610
    }
1611
1612
    /**
1613
     * Test for the createContent() method.
1614
     *
1615
     * Testing multiple languages with multiple translatable fields with empty default value.
1616
     *
1617
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1618
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1619
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1620
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1621
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1622
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet2
1623
     */
1624
    public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields)
1625
    {
1626
        $fieldDefinitions = [
1627
            new FieldDefinition(
1628
                [
1629
                    'id' => 'fieldDefinitionId1',
1630
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1631
                    'isTranslatable' => true,
1632
                    'identifier' => 'identifier1',
1633
                    'isRequired' => false,
1634
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1635
                ]
1636
            ),
1637
            new FieldDefinition(
1638
                [
1639
                    'id' => 'fieldDefinitionId2',
1640
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1641
                    'isTranslatable' => true,
1642
                    'identifier' => 'identifier2',
1643
                    'isRequired' => false,
1644
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1645
                ]
1646
            ),
1647
        ];
1648
1649
        $this->assertForTestCreateContentNonRedundantFieldSet(
1650
            $mainLanguageCode,
1651
            $structFields,
1652
            $spiFields,
1653
            $fieldDefinitions
1654
        );
1655
    }
1656
1657
    public function providerForTestCreateContentNonRedundantFieldSetComplex()
1658
    {
1659
        $spiFields0 = [
1660
            new SPIField(
1661
                [
1662
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1663
                    'type' => 'fieldTypeIdentifier',
1664
                    'value' => 'defaultValue2',
1665
                    'languageCode' => 'eng-US',
1666
                ]
1667
            ),
1668
            new SPIField(
1669
                [
1670
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1671
                    'type' => 'fieldTypeIdentifier',
1672
                    'value' => 'defaultValue4',
1673
                    'languageCode' => 'eng-US',
1674
                ]
1675
            ),
1676
        ];
1677
        $spiFields1 = [
1678
            new SPIField(
1679
                [
1680
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1681
                    'type' => 'fieldTypeIdentifier',
1682
                    'value' => 'newValue1',
1683
                    'languageCode' => 'ger-DE',
1684
                ]
1685
            ),
1686
            new SPIField(
1687
                [
1688
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1689
                    'type' => 'fieldTypeIdentifier',
1690
                    'value' => 'defaultValue2',
1691
                    'languageCode' => 'ger-DE',
1692
                ]
1693
            ),
1694
            new SPIField(
1695
                [
1696
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1697
                    'type' => 'fieldTypeIdentifier',
1698
                    'value' => 'newValue2',
1699
                    'languageCode' => 'eng-US',
1700
                ]
1701
            ),
1702
            new SPIField(
1703
                [
1704
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1705
                    'type' => 'fieldTypeIdentifier',
1706
                    'value' => 'newValue4',
1707
                    'languageCode' => 'eng-US',
1708
                ]
1709
            ),
1710
        ];
1711
1712
        return [
1713
            // 0. Creating by default values only
1714
            [
1715
                'eng-US',
1716
                [],
1717
                $spiFields0,
1718
            ],
1719
            // 1. Multiple languages with language set
1720
            [
1721
                'eng-US',
1722
                [
1723
                    new Field(
1724
                        [
1725
                            'fieldDefIdentifier' => 'identifier1',
1726
                            'value' => 'newValue1',
1727
                            'languageCode' => 'ger-DE',
1728
                        ]
1729
                    ),
1730
                    new Field(
1731
                        [
1732
                            'fieldDefIdentifier' => 'identifier2',
1733
                            'value' => 'newValue2',
1734
                            'languageCode' => 'eng-US',
1735
                        ]
1736
                    ),
1737
                    new Field(
1738
                        [
1739
                            'fieldDefIdentifier' => 'identifier4',
1740
                            'value' => 'newValue4',
1741
                            'languageCode' => 'eng-US',
1742
                        ]
1743
                    ),
1744
                ],
1745
                $spiFields1,
1746
            ],
1747
            // 2. Multiple languages without language set
1748
            [
1749
                'eng-US',
1750
                [
1751
                    new Field(
1752
                        [
1753
                            'fieldDefIdentifier' => 'identifier1',
1754
                            'value' => 'newValue1',
1755
                            'languageCode' => 'ger-DE',
1756
                        ]
1757
                    ),
1758
                    new Field(
1759
                        [
1760
                            'fieldDefIdentifier' => 'identifier2',
1761
                            'value' => 'newValue2',
1762
                            'languageCode' => null,
1763
                        ]
1764
                    ),
1765
                    new Field(
1766
                        [
1767
                            'fieldDefIdentifier' => 'identifier4',
1768
                            'value' => 'newValue4',
1769
                            'languageCode' => null,
1770
                        ]
1771
                    ),
1772
                ],
1773
                $spiFields1,
1774
            ],
1775
        ];
1776
    }
1777
1778
    protected function fixturesForTestCreateContentNonRedundantFieldSetComplex()
1779
    {
1780
        return [
1781
            new FieldDefinition(
1782
                [
1783
                    'id' => 'fieldDefinitionId1',
1784
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1785
                    'isTranslatable' => true,
1786
                    'identifier' => 'identifier1',
1787
                    'isRequired' => false,
1788
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1789
                ]
1790
            ),
1791
            new FieldDefinition(
1792
                [
1793
                    'id' => 'fieldDefinitionId2',
1794
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1795
                    'isTranslatable' => true,
1796
                    'identifier' => 'identifier2',
1797
                    'isRequired' => false,
1798
                    'defaultValue' => 'defaultValue2',
1799
                ]
1800
            ),
1801
            new FieldDefinition(
1802
                [
1803
                    'id' => 'fieldDefinitionId3',
1804
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1805
                    'isTranslatable' => false,
1806
                    'identifier' => 'identifier3',
1807
                    'isRequired' => false,
1808
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1809
                ]
1810
            ),
1811
            new FieldDefinition(
1812
                [
1813
                    'id' => 'fieldDefinitionId4',
1814
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1815
                    'isTranslatable' => false,
1816
                    'identifier' => 'identifier4',
1817
                    'isRequired' => false,
1818
                    'defaultValue' => 'defaultValue4',
1819
                ]
1820
            ),
1821
        ];
1822
    }
1823
1824
    /**
1825
     * Test for the createContent() method.
1826
     *
1827
     * Testing multiple languages with multiple translatable fields with empty default value.
1828
     *
1829
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1830
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1831
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1832
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1833
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1834
     * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex
1835
     */
1836
    public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields)
1837
    {
1838
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
1839
1840
        $this->assertForTestCreateContentNonRedundantFieldSet(
1841
            $mainLanguageCode,
1842
            $structFields,
1843
            $spiFields,
1844
            $fieldDefinitions
1845
        );
1846
    }
1847
1848
    public function providerForTestCreateContentWithInvalidLanguage()
1849
    {
1850
        return [
1851
            [
1852
                'eng-GB',
1853
                [
1854
                    new Field(
1855
                        [
1856
                            'fieldDefIdentifier' => 'identifier',
1857
                            'value' => 'newValue',
1858
                            'languageCode' => 'Klingon',
1859
                        ]
1860
                    ),
1861
                ],
1862
            ],
1863
            [
1864
                'Klingon',
1865
                [
1866
                    new Field(
1867
                        [
1868
                            'fieldDefIdentifier' => 'identifier',
1869
                            'value' => 'newValue',
1870
                            'languageCode' => 'eng-GB',
1871
                        ]
1872
                    ),
1873
                ],
1874
            ],
1875
        ];
1876
    }
1877
1878
    /**
1879
     * Test for the updateContent() method.
1880
     *
1881
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1882
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1883
     * @dataProvider providerForTestCreateContentWithInvalidLanguage
1884
     */
1885
    public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields)
1886
    {
1887
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
1888
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
1889
1890
        $repositoryMock = $this->getRepositoryMock();
1891
        $mockedService = $this->getPartlyMockedContentService();
1892
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1893
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1894
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1895
        $domainMapperMock = $this->getDomainMapperMock();
1896
        $permissionResolver = $this->getPermissionResolverMock();
1897
1898
        $contentType = new ContentType(
1899
            [
1900
                'id' => 123,
1901
                'fieldDefinitions' => [],
1902
            ]
1903
        );
1904
        $contentCreateStruct = new ContentCreateStruct(
1905
            [
1906
                'fields' => $structFields,
1907
                'mainLanguageCode' => $mainLanguageCode,
1908
                'contentType' => $contentType,
1909
                'alwaysAvailable' => false,
1910
                'ownerId' => 169,
1911
                'sectionId' => 1,
1912
            ]
1913
        );
1914
1915
        $languageHandlerMock->expects($this->any())
1916
            ->method('loadByLanguageCode')
1917
            ->with($this->isType('string'))
1918
            ->will(
1919
                $this->returnCallback(
1920
                    function ($languageCode) {
1921
                        if ($languageCode === 'Klingon') {
1922
                            throw new NotFoundException('Language', 'Klingon');
1923
                        }
1924
1925
                        return new Language(['id' => 4242]);
1926
                    }
1927
                )
1928
            );
1929
1930
        $contentTypeServiceMock->expects($this->once())
1931
            ->method('loadContentType')
1932
            ->with($this->equalTo($contentType->id))
1933
            ->will($this->returnValue($contentType));
1934
1935
        $repositoryMock->expects($this->once())
1936
            ->method('getContentTypeService')
1937
            ->will($this->returnValue($contentTypeServiceMock));
1938
1939
        $that = $this;
1940
        $permissionResolver->expects($this->once())
1941
            ->method('canUser')
1942
            ->with(
1943
                $this->equalTo('content'),
1944
                $this->equalTo('create'),
1945
                $this->isInstanceOf(APIContentCreateStruct::class),
1946
                $this->equalTo([])
1947
            )->will(
1948
                $this->returnCallback(
1949
                    function () use ($that, $contentCreateStruct) {
1950
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1951
1952
                        return true;
1953
                    }
1954
                )
1955
            );
1956
1957
        $domainMapperMock->expects($this->once())
1958
            ->method('getUniqueHash')
1959
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1960
            ->will(
1961
                $this->returnCallback(
1962
                    function ($object) use ($that, $contentCreateStruct) {
1963
                        $that->assertEquals($contentCreateStruct, $object);
1964
1965
                        return 'hash';
1966
                    }
1967
                )
1968
            );
1969
1970
        $mockedService->createContent($contentCreateStruct, []);
1971
    }
1972
1973
    protected function assertForCreateContentContentValidationException(
1974
        $mainLanguageCode,
1975
        $structFields,
1976
        $fieldDefinitions = []
1977
    ) {
1978
        $repositoryMock = $this->getRepositoryMock();
1979
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
1980
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1981
        $permissionResolver = $this->getPermissionResolverMock();
1982
1983
        $contentType = new ContentType(
1984
            [
1985
                'id' => 123,
1986
                'fieldDefinitions' => $fieldDefinitions,
1987
            ]
1988
        );
1989
        $contentCreateStruct = new ContentCreateStruct(
1990
            [
1991
                'ownerId' => 169,
1992
                'alwaysAvailable' => false,
1993
                'remoteId' => 'faraday',
1994
                'mainLanguageCode' => $mainLanguageCode,
1995
                'fields' => $structFields,
1996
                'contentType' => $contentType,
1997
            ]
1998
        );
1999
2000
        $contentTypeServiceMock->expects($this->once())
2001
            ->method('loadContentType')
2002
            ->with($this->equalTo(123))
2003
            ->will($this->returnValue($contentType));
2004
2005
        $repositoryMock->expects($this->once())
2006
            ->method('getContentTypeService')
2007
            ->will($this->returnValue($contentTypeServiceMock));
2008
2009
        $permissionResolver->expects($this->once())
2010
            ->method('canUser')
2011
            ->with(
2012
                $this->equalTo('content'),
2013
                $this->equalTo('create'),
2014
                $this->isInstanceOf(get_class($contentCreateStruct)),
2015
                $this->equalTo([])
2016
            )->will($this->returnValue(true));
2017
2018
        $mockedService->expects($this->once())
2019
            ->method('loadContentByRemoteId')
2020
            ->with($contentCreateStruct->remoteId)
2021
            ->will(
2022
                $this->throwException(new NotFoundException('Content', 'faraday'))
2023
            );
2024
2025
        $mockedService->createContent($contentCreateStruct, []);
2026
    }
2027
2028
    public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition()
2029
    {
2030
        return [
2031
            [
2032
                'eng-GB',
2033
                [
2034
                    new Field(
2035
                        [
2036
                            'fieldDefIdentifier' => 'identifier',
2037
                            'value' => 'newValue',
2038
                            'languageCode' => 'eng-GB',
2039
                        ]
2040
                    ),
2041
                ],
2042
            ],
2043
        ];
2044
    }
2045
2046
    /**
2047
     * Test for the createContent() method.
2048
     *
2049
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2050
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2051
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2052
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition
2053
     */
2054
    public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields)
2055
    {
2056
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
2057
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
2058
2059
        $this->assertForCreateContentContentValidationException(
2060
            $mainLanguageCode,
2061
            $structFields,
2062
            []
2063
        );
2064
    }
2065
2066
    public function providerForTestCreateContentThrowsContentValidationExceptionTranslation()
2067
    {
2068
        return [
2069
            [
2070
                'eng-GB',
2071
                [
2072
                    new Field(
2073
                        [
2074
                            'fieldDefIdentifier' => 'identifier',
2075
                            'value' => 'newValue',
2076
                            'languageCode' => 'eng-US',
2077
                        ]
2078
                    ),
2079
                ],
2080
            ],
2081
        ];
2082
    }
2083
2084
    /**
2085
     * Test for the createContent() method.
2086
     *
2087
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2088
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2089
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2090
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2091
     */
2092
    public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields)
2093
    {
2094
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
2095
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
2096
2097
        $fieldDefinitions = [
2098
            new FieldDefinition(
2099
                [
2100
                    'id' => 'fieldDefinitionId1',
2101
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2102
                    'isTranslatable' => false,
2103
                    'identifier' => 'identifier',
2104
                    'isRequired' => false,
2105
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
2106
                ]
2107
            ),
2108
        ];
2109
2110
        $this->assertForCreateContentContentValidationException(
2111
            $mainLanguageCode,
2112
            $structFields,
2113
            $fieldDefinitions
2114
        );
2115
    }
2116
2117
    /**
2118
     * Asserts behaviour necessary for testing ContentFieldValidationException because of required
2119
     * field being empty.
2120
     *
2121
     * @param string $mainLanguageCode
2122
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2123
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2124
     *
2125
     * @return mixed
2126
     */
2127
    protected function assertForTestCreateContentRequiredField(
2128
        $mainLanguageCode,
2129
        array $structFields,
2130
        array $fieldDefinitions
2131
    ) {
2132
        $repositoryMock = $this->getRepositoryMock();
2133
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2134
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2135
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2136
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock 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...
2137
        $domainMapperMock = $this->getDomainMapperMock();
2138
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2139
        $permissionResolver = $this->getPermissionResolverMock();
2140
2141
        $contentType = new ContentType(
2142
            [
2143
                'id' => 123,
2144
                'fieldDefinitions' => $fieldDefinitions,
2145
                'nameSchema' => '<nameSchema>',
2146
            ]
2147
        );
2148
        $contentCreateStruct = new ContentCreateStruct(
2149
            [
2150
                'fields' => $structFields,
2151
                'mainLanguageCode' => $mainLanguageCode,
2152
                'contentType' => $contentType,
2153
                'alwaysAvailable' => false,
2154
                'ownerId' => 169,
2155
                'sectionId' => 1,
2156
            ]
2157
        );
2158
2159
        $languageHandlerMock->expects($this->any())
2160
            ->method('loadByLanguageCode')
2161
            ->with($this->isType('string'))
2162
            ->will(
2163
                $this->returnCallback(
2164
                    function () {
2165
                        return new Language(['id' => 4242]);
2166
                    }
2167
                )
2168
            );
2169
2170
        $contentTypeServiceMock->expects($this->once())
2171
            ->method('loadContentType')
2172
            ->with($this->equalTo($contentType->id))
2173
            ->will($this->returnValue($contentType));
2174
2175
        $repositoryMock->expects($this->once())
2176
            ->method('getContentTypeService')
2177
            ->will($this->returnValue($contentTypeServiceMock));
2178
2179
        $that = $this;
2180
        $permissionResolver->expects($this->once())
2181
            ->method('canUser')
2182
            ->with(
2183
                $this->equalTo('content'),
2184
                $this->equalTo('create'),
2185
                $this->isInstanceOf(APIContentCreateStruct::class),
2186
                $this->equalTo([])
2187
            )->will(
2188
                $this->returnCallback(
2189
                    function () use ($that, $contentCreateStruct) {
2190
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2191
2192
                        return true;
2193
                    }
2194
                )
2195
            );
2196
2197
        $domainMapperMock->expects($this->once())
2198
            ->method('getUniqueHash')
2199
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2200
            ->will(
2201
                $this->returnCallback(
2202
                    function ($object) use ($that, $contentCreateStruct) {
2203
                        $that->assertEquals($contentCreateStruct, $object);
2204
2205
                        return 'hash';
2206
                    }
2207
                )
2208
            );
2209
2210
        $fieldTypeMock->expects($this->any())
2211
            ->method('acceptValue')
2212
            ->will(
2213
                $this->returnCallback(
2214
                    function ($valueString) {
2215
                        return new ValueStub($valueString);
2216
                    }
2217
                )
2218
            );
2219
2220
        $emptyValue = self::EMPTY_FIELD_VALUE;
2221
        $fieldTypeMock->expects($this->any())
2222
            ->method('isEmptyValue')
2223
            ->will(
2224
                $this->returnCallback(
2225
                    function (ValueStub $value) use ($emptyValue) {
2226
                        return $emptyValue === (string)$value;
2227
                    }
2228
                )
2229
            );
2230
2231
        $fieldTypeMock->expects($this->any())
2232
            ->method('validate')
2233
            ->will($this->returnValue([]));
2234
2235
        $this->getFieldTypeRegistryMock()->expects($this->any())
2236
            ->method('getFieldType')
2237
            ->will($this->returnValue($fieldTypeMock));
2238
2239
        return $contentCreateStruct;
2240
    }
2241
2242
    public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField()
2243
    {
2244
        return [
2245
            [
2246
                'eng-US',
2247
                [
2248
                    new Field(
2249
                        [
2250
                            'fieldDefIdentifier' => 'identifier',
2251
                            'value' => self::EMPTY_FIELD_VALUE,
2252
                            'languageCode' => null,
2253
                        ]
2254
                    ),
2255
                ],
2256
                'identifier',
2257
                'eng-US',
2258
            ],
2259
        ];
2260
    }
2261
2262
    /**
2263
     * Test for the createContent() method.
2264
     *
2265
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2266
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2267
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2268
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField
2269
     */
2270
    public function testCreateContentRequiredField(
2271
        $mainLanguageCode,
2272
        $structFields,
2273
        $identifier,
2274
        $languageCode
2275
    ) {
2276
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2277
2278
        $fieldDefinitions = [
2279
            new FieldDefinition(
2280
                [
2281
                    'id' => 'fieldDefinitionId',
2282
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2283
                    'isTranslatable' => true,
2284
                    'identifier' => 'identifier',
2285
                    'isRequired' => true,
2286
                    'defaultValue' => 'defaultValue',
2287
                ]
2288
            ),
2289
        ];
2290
        $contentCreateStruct = $this->assertForTestCreateContentRequiredField(
2291
            $mainLanguageCode,
2292
            $structFields,
2293
            $fieldDefinitions
2294
        );
2295
2296
        $mockedService = $this->getPartlyMockedContentService();
2297
2298
        try {
2299
            $mockedService->createContent($contentCreateStruct, []);
2300
        } catch (ContentValidationException $e) {
2301
            $this->assertEquals(
2302
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
2303
                $e->getMessage()
2304
            );
2305
2306
            throw $e;
2307
        }
2308
    }
2309
2310
    /**
2311
     * Asserts behaviour necessary for testing ContentFieldValidationException because of
2312
     * field not being valid.
2313
     *
2314
     * @param string $mainLanguageCode
2315
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2316
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2317
     *
2318
     * @return mixed
2319
     */
2320
    protected function assertForTestCreateContentThrowsContentFieldValidationException(
2321
        $mainLanguageCode,
2322
        array $structFields,
2323
        array $fieldDefinitions
2324
    ) {
2325
        $repositoryMock = $this->getRepositoryMock();
2326
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2327
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2328
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2329
        $fieldTypeServiceMock = $this->getFieldTypeServiceMock();
0 ignored issues
show
Unused Code introduced by
$fieldTypeServiceMock 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...
2330
        $domainMapperMock = $this->getDomainMapperMock();
2331
        $relationProcessorMock = $this->getRelationProcessorMock();
2332
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2333
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
2334
        $permissionResolver = $this->getPermissionResolverMock();
2335
2336
        $contentType = new ContentType(
2337
            [
2338
                'id' => 123,
2339
                'fieldDefinitions' => $fieldDefinitions,
2340
                'nameSchema' => '<nameSchema>',
2341
            ]
2342
        );
2343
        $contentCreateStruct = new ContentCreateStruct(
2344
            [
2345
                'fields' => $structFields,
2346
                'mainLanguageCode' => $mainLanguageCode,
2347
                'contentType' => $contentType,
2348
                'alwaysAvailable' => false,
2349
                'ownerId' => 169,
2350
                'sectionId' => 1,
2351
            ]
2352
        );
2353
2354
        $languageHandlerMock->expects($this->any())
2355
            ->method('loadByLanguageCode')
2356
            ->with($this->isType('string'))
2357
            ->will(
2358
                $this->returnCallback(
2359
                    function () {
2360
                        return new Language(['id' => 4242]);
2361
                    }
2362
                )
2363
            );
2364
2365
        $contentTypeServiceMock->expects($this->once())
2366
            ->method('loadContentType')
2367
            ->with($this->equalTo($contentType->id))
2368
            ->will($this->returnValue($contentType));
2369
2370
        $repositoryMock->expects($this->once())
2371
            ->method('getContentTypeService')
2372
            ->will($this->returnValue($contentTypeServiceMock));
2373
2374
        $that = $this;
2375
        $permissionResolver->expects($this->once())
2376
            ->method('canUser')
2377
            ->with(
2378
                $this->equalTo('content'),
2379
                $this->equalTo('create'),
2380
                $this->isInstanceOf(APIContentCreateStruct::class),
2381
                $this->equalTo([])
2382
            )->will(
2383
                $this->returnCallback(
2384
                    function () use ($that, $contentCreateStruct) {
2385
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2386
2387
                        return true;
2388
                    }
2389
                )
2390
            );
2391
2392
        $domainMapperMock->expects($this->once())
2393
            ->method('getUniqueHash')
2394
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2395
            ->will(
2396
                $this->returnCallback(
2397
                    function ($object) use ($that, $contentCreateStruct) {
2398
                        $that->assertEquals($contentCreateStruct, $object);
2399
2400
                        return 'hash';
2401
                    }
2402
                )
2403
            );
2404
2405
        $this->getFieldTypeRegistryMock()->expects($this->any())
2406
            ->method('getFieldType')
2407
            ->will($this->returnValue($fieldTypeMock));
2408
2409
        $relationProcessorMock
2410
            ->expects($this->any())
2411
            ->method('appendFieldRelations')
2412
            ->with(
2413
                $this->isType('array'),
2414
                $this->isType('array'),
2415
                $this->isInstanceOf(SPIFieldType::class),
2416
                $this->isInstanceOf(Value::class),
2417
                $this->anything()
2418
            );
2419
2420
        $fieldValues = $this->determineValuesForCreate(
2421
            $mainLanguageCode,
2422
            $structFields,
2423
            $fieldDefinitions,
2424
            $languageCodes
2425
        );
2426
        $allFieldErrors = [];
2427
        $validateCount = 0;
2428
        $emptyValue = self::EMPTY_FIELD_VALUE;
2429
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
2430
            foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) {
2431
                $fieldTypeMock->expects($this->at($validateCount++))
2432
                    ->method('acceptValue')
2433
                    ->will(
2434
                        $this->returnCallback(
2435
                            function ($valueString) {
2436
                                return new ValueStub($valueString);
2437
                            }
2438
                        )
2439
                    );
2440
2441
                $fieldTypeMock->expects($this->at($validateCount++))
2442
                    ->method('isEmptyValue')
2443
                    ->will(
2444
                        $this->returnCallback(
2445
                            function (ValueStub $value) use ($emptyValue) {
2446
                                return $emptyValue === (string)$value;
2447
                            }
2448
                        )
2449
                    );
2450
2451
                if (self::EMPTY_FIELD_VALUE === (string)$value) {
2452
                    continue;
2453
                }
2454
2455
                $fieldTypeMock->expects($this->at($validateCount++))
2456
                    ->method('validate')
2457
                    ->with(
2458
                        $this->equalTo($fieldDefinition),
2459
                        $this->equalTo($value)
2460
                    )->will($this->returnArgument(1));
2461
2462
                $allFieldErrors[$fieldDefinition->id][$languageCode] = $value;
2463
            }
2464
        }
2465
2466
        return [$contentCreateStruct, $allFieldErrors];
2467
    }
2468
2469
    public function providerForTestCreateContentThrowsContentFieldValidationException()
2470
    {
2471
        return $this->providerForTestCreateContentNonRedundantFieldSetComplex();
2472
    }
2473
2474
    /**
2475
     * Test for the createContent() method.
2476
     *
2477
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2478
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2479
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2480
     * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException
2481
     */
2482
    public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields)
2483
    {
2484
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
2485
        $this->expectExceptionMessage('Content fields did not validate');
2486
2487
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
2488
        list($contentCreateStruct, $allFieldErrors) =
2489
            $this->assertForTestCreateContentThrowsContentFieldValidationException(
2490
                $mainLanguageCode,
2491
                $structFields,
2492
                $fieldDefinitions
2493
            );
2494
2495
        $mockedService = $this->getPartlyMockedContentService();
2496
2497
        try {
2498
            $mockedService->createContent($contentCreateStruct);
2499
        } catch (ContentFieldValidationException $e) {
2500
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
2501
            throw $e;
2502
        }
2503
    }
2504
2505
    /**
2506
     * Test for the createContent() method.
2507
     *
2508
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2509
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2510
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2511
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2512
     */
2513
    public function testCreateContentWithLocations()
2514
    {
2515
        $spiFields = [
2516
            new SPIField(
2517
                [
2518
                    'fieldDefinitionId' => 'fieldDefinitionId',
2519
                    'type' => 'fieldTypeIdentifier',
2520
                    'value' => 'defaultValue',
2521
                    'languageCode' => 'eng-US',
2522
                ]
2523
            ),
2524
        ];
2525
        $fieldDefinitions = [
2526
            new FieldDefinition(
2527
                [
2528
                    'id' => 'fieldDefinitionId',
2529
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2530
                    'isTranslatable' => false,
2531
                    'identifier' => 'identifier',
2532
                    'isRequired' => false,
2533
                    'defaultValue' => 'defaultValue',
2534
                ]
2535
            ),
2536
        ];
2537
2538
        // Set up a simple case that will pass
2539
        $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]);
2540
        $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]);
2541
        $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2];
2542
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2543
            'eng-US',
2544
            [],
2545
            $spiFields,
2546
            $fieldDefinitions,
2547
            $locationCreateStructs,
2548
            false,
2549
            // Do not execute
2550
            false
2551
        );
2552
2553
        $repositoryMock = $this->getRepositoryMock();
2554
        $mockedService = $this->getPartlyMockedContentService();
2555
        $locationServiceMock = $this->getLocationServiceMock();
2556
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2557
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2558
        $domainMapperMock = $this->getDomainMapperMock();
2559
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2560
        $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]);
2561
2562
        $locationServiceMock->expects($this->at(0))
2563
            ->method('loadLocation')
2564
            ->with($this->equalTo(321))
2565
            ->will($this->returnValue($parentLocation));
2566
2567
        $locationServiceMock->expects($this->at(1))
2568
            ->method('loadLocation')
2569
            ->with($this->equalTo(654))
2570
            ->will($this->returnValue($parentLocation));
2571
2572
        $repositoryMock->expects($this->atLeastOnce())
2573
            ->method('getLocationService')
2574
            ->will($this->returnValue($locationServiceMock));
2575
2576
        $domainMapperMock->expects($this->at(1))
2577
            ->method('buildSPILocationCreateStruct')
2578
            ->with(
2579
                $this->equalTo($locationCreateStruct1),
2580
                $this->equalTo($parentLocation),
2581
                $this->equalTo(true),
2582
                $this->equalTo(null),
2583
                $this->equalTo(null)
2584
            )->will($this->returnValue($spiLocationCreateStruct));
2585
2586
        $domainMapperMock->expects($this->at(2))
2587
            ->method('buildSPILocationCreateStruct')
2588
            ->with(
2589
                $this->equalTo($locationCreateStruct2),
2590
                $this->equalTo($parentLocation),
2591
                $this->equalTo(false),
2592
                $this->equalTo(null),
2593
                $this->equalTo(null)
2594
            )->will($this->returnValue($spiLocationCreateStruct));
2595
2596
        $spiContentCreateStruct = new SPIContentCreateStruct(
2597
            [
2598
                'name' => [],
2599
                'typeId' => 123,
2600
                'sectionId' => 1,
2601
                'ownerId' => 169,
2602
                'remoteId' => 'hash',
2603
                'fields' => $spiFields,
2604
                'modified' => time(),
2605
                'initialLanguageId' => 4242,
2606
                'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct],
2607
            ]
2608
        );
2609
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2610
        ++$spiContentCreateStruct2->modified;
2611
2612
        $spiContent = new SPIContent(
2613
            [
2614
                'versionInfo' => new SPIContent\VersionInfo(
2615
                    [
2616
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2617
                        'versionNo' => 7,
2618
                    ]
2619
                ),
2620
            ]
2621
        );
2622
2623
        $handlerMock->expects($this->once())
2624
            ->method('create')
2625
            ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
2626
            ->will($this->returnValue($spiContent));
2627
2628
        $domainMapperMock->expects($this->once())
2629
            ->method('buildContentDomainObject')
2630
            ->with(
2631
                $this->isInstanceOf(SPIContent::class),
2632
                $this->isInstanceOf(APIContentType::class)
2633
            )
2634
            ->willReturn($this->createMock(APIContent::class));
2635
2636
        $repositoryMock->expects($this->once())->method('commit');
2637
2638
        // Execute
2639
        $mockedService->createContent($contentCreateStruct, $locationCreateStructs);
2640
    }
2641
2642
    /**
2643
     * Test for the createContent() method.
2644
     *
2645
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2646
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2647
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2648
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2649
     */
2650
    public function testCreateContentWithLocationsDuplicateUnderParent()
2651
    {
2652
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
2653
        $this->expectExceptionMessage('Multiple LocationCreateStructs with the same parent Location \'321\' are given');
2654
2655
        $fieldDefinitions = [
2656
            new FieldDefinition(
2657
                [
2658
                    'id' => 'fieldDefinitionId',
2659
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2660
                    'isTranslatable' => false,
2661
                    'identifier' => 'identifier',
2662
                    'isRequired' => false,
2663
                    'defaultValue' => 'defaultValue',
2664
                ]
2665
            ),
2666
        ];
2667
2668
        $repositoryMock = $this->getRepositoryMock();
2669
        $mockedService = $this->getPartlyMockedContentService();
2670
        $locationServiceMock = $this->getLocationServiceMock();
2671
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2672
        $domainMapperMock = $this->getDomainMapperMock();
2673
        $permissionResolver = $this->getPermissionResolverMock();
2674
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2675
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2676
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2677
        $parentLocation = new Location(['id' => 321]);
2678
        $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]);
2679
        $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct];
2680
        $contentType = new ContentType(
2681
            [
2682
                'id' => 123,
2683
                'fieldDefinitions' => $fieldDefinitions,
2684
                'nameSchema' => '<nameSchema>',
2685
            ]
2686
        );
2687
        $contentCreateStruct = new ContentCreateStruct(
2688
            [
2689
                'fields' => [],
2690
                'mainLanguageCode' => 'eng-US',
2691
                'contentType' => $contentType,
2692
                'alwaysAvailable' => false,
2693
                'ownerId' => 169,
2694
                'sectionId' => 1,
2695
            ]
2696
        );
2697
2698
        $languageHandlerMock->expects($this->any())
2699
            ->method('loadByLanguageCode')
2700
            ->with($this->isType('string'))
2701
            ->will(
2702
                $this->returnCallback(
2703
                    function () {
2704
                        return new Language(['id' => 4242]);
2705
                    }
2706
                )
2707
            );
2708
2709
        $contentTypeServiceMock->expects($this->once())
2710
            ->method('loadContentType')
2711
            ->with($this->equalTo($contentType->id))
2712
            ->will($this->returnValue($contentType));
2713
2714
        $repositoryMock->expects($this->once())
2715
            ->method('getContentTypeService')
2716
            ->will($this->returnValue($contentTypeServiceMock));
2717
2718
        $that = $this;
2719
        $permissionResolver->expects($this->once())
2720
            ->method('canUser')
2721
            ->with(
2722
                $this->equalTo('content'),
2723
                $this->equalTo('create'),
2724
                $this->isInstanceOf(APIContentCreateStruct::class),
2725
                $this->equalTo($locationCreateStructs)
2726
            )->will(
2727
                $this->returnCallback(
2728
                    function () use ($that, $contentCreateStruct) {
2729
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2730
2731
                        return true;
2732
                    }
2733
                )
2734
            );
2735
2736
        $domainMapperMock->expects($this->once())
2737
            ->method('getUniqueHash')
2738
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2739
            ->will(
2740
                $this->returnCallback(
2741
                    function ($object) use ($that, $contentCreateStruct) {
2742
                        $that->assertEquals($contentCreateStruct, $object);
2743
2744
                        return 'hash';
2745
                    }
2746
                )
2747
            );
2748
2749
        $locationServiceMock->expects($this->once())
2750
            ->method('loadLocation')
2751
            ->with($this->equalTo(321))
2752
            ->will($this->returnValue($parentLocation));
2753
2754
        $repositoryMock->expects($this->any())
2755
            ->method('getLocationService')
2756
            ->will($this->returnValue($locationServiceMock));
2757
2758
        $domainMapperMock->expects($this->any())
2759
            ->method('buildSPILocationCreateStruct')
2760
            ->with(
2761
                $this->equalTo($locationCreateStruct),
2762
                $this->equalTo($parentLocation),
2763
                $this->equalTo(true),
2764
                $this->equalTo(null),
2765
                $this->equalTo(null)
2766
            )->will($this->returnValue($spiLocationCreateStruct));
2767
2768
        $mockedService->createContent(
2769
            $contentCreateStruct,
2770
            $locationCreateStructs
2771
        );
2772
    }
2773
2774
    /**
2775
     * Test for the createContent() method.
2776
     *
2777
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2778
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2779
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2780
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2781
     */
2782
    public function testCreateContentObjectStates()
2783
    {
2784
        $spiFields = [
2785
            new SPIField(
2786
                [
2787
                    'fieldDefinitionId' => 'fieldDefinitionId',
2788
                    'type' => 'fieldTypeIdentifier',
2789
                    'value' => 'defaultValue',
2790
                    'languageCode' => 'eng-US',
2791
                ]
2792
            ),
2793
        ];
2794
        $fieldDefinitions = [
2795
            new FieldDefinition(
2796
                [
2797
                    'id' => 'fieldDefinitionId',
2798
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2799
                    'isTranslatable' => false,
2800
                    'identifier' => 'identifier',
2801
                    'isRequired' => false,
2802
                    'defaultValue' => 'defaultValue',
2803
                ]
2804
            ),
2805
        ];
2806
        $objectStateGroups = [
0 ignored issues
show
Unused Code introduced by
$objectStateGroups 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...
2807
            new SPIObjectStateGroup(['id' => 10]),
2808
            new SPIObjectStateGroup(['id' => 20]),
2809
        ];
2810
2811
        // Set up a simple case that will pass
2812
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2813
            'eng-US',
2814
            [],
2815
            $spiFields,
2816
            $fieldDefinitions,
2817
            [],
2818
            true,
2819
            // Do not execute
2820
            false
2821
        );
2822
        $timestamp = time();
2823
        $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}");
2824
2825
        $repositoryMock = $this->getRepositoryMock();
2826
        $mockedService = $this->getPartlyMockedContentService();
2827
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2828
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2829
        $domainMapperMock = $this->getDomainMapperMock();
2830
2831
        $this->mockGetDefaultObjectStates();
2832
        $this->mockSetDefaultObjectStates();
2833
2834
        $spiContentCreateStruct = new SPIContentCreateStruct(
2835
            [
2836
                'name' => [],
2837
                'typeId' => 123,
2838
                'sectionId' => 1,
2839
                'ownerId' => 169,
2840
                'remoteId' => 'hash',
2841
                'fields' => $spiFields,
2842
                'modified' => $timestamp,
2843
                'initialLanguageId' => 4242,
2844
                'locations' => [],
2845
            ]
2846
        );
2847
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2848
        ++$spiContentCreateStruct2->modified;
2849
2850
        $spiContent = new SPIContent(
2851
            [
2852
                'versionInfo' => new SPIContent\VersionInfo(
2853
                    [
2854
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2855
                        'versionNo' => 7,
2856
                    ]
2857
                ),
2858
            ]
2859
        );
2860
2861
        $handlerMock->expects($this->once())
2862
            ->method('create')
2863
            ->with($this->equalTo($spiContentCreateStruct))
2864
            ->will($this->returnValue($spiContent));
2865
2866
        $domainMapperMock->expects($this->once())
2867
            ->method('buildContentDomainObject')
2868
            ->with(
2869
                $this->isInstanceOf(SPIContent::class),
2870
                $this->isInstanceOf(APIContentType::class)
2871
            )
2872
            ->willReturn($this->createMock(APIContent::class));
2873
2874
        $repositoryMock->expects($this->once())->method('commit');
2875
2876
        // Execute
2877
        $mockedService->createContent($contentCreateStruct, []);
2878
    }
2879
2880
    /**
2881
     * Test for the createContent() method.
2882
     *
2883
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2884
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2885
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2886
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2887
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2888
     */
2889
    public function testCreateContentWithRollback()
2890
    {
2891
        $this->expectException(\Exception::class);
2892
        $this->expectExceptionMessage('Store failed');
2893
2894
        $fieldDefinitions = [
2895
            new FieldDefinition(
2896
                [
2897
                    'id' => 'fieldDefinitionId',
2898
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2899
                    'isTranslatable' => false,
2900
                    'identifier' => 'identifier',
2901
                    'isRequired' => false,
2902
                    'defaultValue' => 'defaultValue',
2903
                ]
2904
            ),
2905
        ];
2906
2907
        // Setup a simple case that will pass
2908
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2909
            'eng-US',
2910
            [],
2911
            [],
2912
            $fieldDefinitions,
2913
            [],
2914
            false,
2915
            // Do not execute test
2916
            false
2917
        );
2918
2919
        $repositoryMock = $this->getRepositoryMock();
2920
        $repositoryMock->expects($this->never())->method('commit');
2921
        $repositoryMock->expects($this->once())->method('rollback');
2922
2923
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
2924
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
2925
        $contentHandlerMock->expects($this->once())
2926
            ->method('create')
2927
            ->with($this->anything())
2928
            ->will($this->throwException(new \Exception('Store failed')));
2929
2930
        // Execute
2931
        $this->partlyMockedContentService->createContent($contentCreateStruct, []);
2932
    }
2933
2934
    public function providerForTestUpdateContentThrowsBadStateException()
2935
    {
2936
        return [
2937
            [VersionInfo::STATUS_PUBLISHED],
2938
            [VersionInfo::STATUS_ARCHIVED],
2939
        ];
2940
    }
2941
2942
    /**
2943
     * Test for the updateContent() method.
2944
     *
2945
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2946
     * @dataProvider providerForTestUpdateContentThrowsBadStateException
2947
     */
2948
    public function testUpdateContentThrowsBadStateException($status)
2949
    {
2950
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class);
2951
2952
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2953
        $contentUpdateStruct = new ContentUpdateStruct();
2954
        $versionInfo = new VersionInfo(
2955
            [
2956
                'contentInfo' => new ContentInfo(['id' => 42]),
2957
                'versionNo' => 7,
2958
                'status' => $status,
2959
            ]
2960
        );
2961
        $content = new Content(
2962
            [
2963
                'versionInfo' => $versionInfo,
2964
                'internalFields' => [],
2965
            ]
2966
        );
2967
2968
        $mockedService->expects($this->once())
2969
            ->method('loadContent')
2970
            ->with(
2971
                $this->equalTo(42),
2972
                $this->equalTo(null),
2973
                $this->equalTo(7)
2974
            )->will(
2975
                $this->returnValue($content)
2976
            );
2977
2978
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2979
    }
2980
2981
    /**
2982
     * Test for the updateContent() method.
2983
     *
2984
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2985
     */
2986
    public function testUpdateContentThrowsUnauthorizedException()
2987
    {
2988
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class);
2989
2990
        $permissionResolverMock = $this->getPermissionResolverMock();
2991
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2992
        $contentUpdateStruct = new ContentUpdateStruct();
2993
        $versionInfo = new VersionInfo(
2994
            [
2995
                'contentInfo' => new ContentInfo(['id' => 42]),
2996
                'versionNo' => 7,
2997
                'status' => VersionInfo::STATUS_DRAFT,
2998
            ]
2999
        );
3000
        $content = new Content(
3001
            [
3002
                'versionInfo' => $versionInfo,
3003
                'internalFields' => [],
3004
            ]
3005
        );
3006
3007
        $mockedService->expects($this->once())
3008
            ->method('loadContent')
3009
            ->with(
3010
                $this->equalTo(42),
3011
                $this->equalTo(null),
3012
                $this->equalTo(7)
3013
            )->will(
3014
                $this->returnValue($content)
3015
            );
3016
3017
        $permissionResolverMock->expects($this->once())
3018
            ->method('canUser')
3019
            ->with(
3020
                $this->equalTo('content'),
3021
                $this->equalTo('edit'),
3022
                $this->equalTo($content),
3023
                $this->isType('array')
3024
            )->will($this->returnValue(false));
3025
3026
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
3027
    }
3028
3029
    /**
3030
     * @param string $initialLanguageCode
3031
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3032
     * @param string[] $existingLanguages
3033
     *
3034
     * @return string[]
3035
     */
3036
    protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages)
3037
    {
3038
        $languageCodes = array_fill_keys($existingLanguages, true);
3039
        if ($initialLanguageCode !== null) {
3040
            $languageCodes[$initialLanguageCode] = true;
3041
        }
3042
3043
        foreach ($structFields as $field) {
3044
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
3045
                continue;
3046
            }
3047
3048
            $languageCodes[$field->languageCode] = true;
3049
        }
3050
3051
        return array_keys($languageCodes);
3052
    }
3053
3054
    /**
3055
     * @param string $initialLanguageCode
3056
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3057
     * @param string $mainLanguageCode
3058
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3059
     *
3060
     * @return array
3061
     */
3062
    protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions)
3063
    {
3064
        $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode;
3065
3066
        $mappedFieldDefinitions = [];
3067
        foreach ($fieldDefinitions as $fieldDefinition) {
3068
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
3069
        }
3070
3071
        $mappedStructFields = [];
3072
        foreach ($structFields as $structField) {
3073
            $identifier = $structField->fieldDefIdentifier;
3074
3075
            if ($structField->languageCode !== null) {
3076
                $languageCode = $structField->languageCode;
3077
            } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) {
3078
                $languageCode = $initialLanguageCode;
3079
            } else {
3080
                $languageCode = $mainLanguageCode;
3081
            }
3082
3083
            $mappedStructFields[$identifier][$languageCode] = (string)$structField->value;
3084
        }
3085
3086
        return $mappedStructFields;
3087
    }
3088
3089
    /**
3090
     * Returns full, possibly redundant array of field values, indexed by field definition
3091
     * identifier and language code.
3092
     *
3093
     * @param string $initialLanguageCode
3094
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3095
     * @param \eZ\Publish\Core\Repository\Values\Content\Content $content
3096
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3097
     * @param array $languageCodes
3098
     *
3099
     * @return array
3100
     */
3101
    protected function determineValuesForUpdate(
3102
        $initialLanguageCode,
3103
        array $structFields,
3104
        Content $content,
3105
        array $fieldDefinitions,
3106
        array $languageCodes
3107
    ) {
3108
        $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode;
3109
3110
        $mappedStructFields = $this->mapStructFieldsForUpdate(
3111
            $initialLanguageCode,
3112
            $structFields,
3113
            $mainLanguageCode,
3114
            $fieldDefinitions
3115
        );
3116
3117
        $values = [];
3118
3119
        foreach ($fieldDefinitions as $fieldDefinition) {
3120
            $identifier = $fieldDefinition->identifier;
3121
            foreach ($languageCodes as $languageCode) {
3122
                if (!$fieldDefinition->isTranslatable) {
3123
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
3124
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
3125
                    } else {
3126
                        $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode];
3127
                    }
3128
                    continue;
3129
                }
3130
3131
                if (isset($mappedStructFields[$identifier][$languageCode])) {
3132
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
3133
                    continue;
3134
                }
3135
3136
                if (isset($content->fields[$identifier][$languageCode])) {
3137
                    $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode];
3138
                    continue;
3139
                }
3140
3141
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
3142
            }
3143
        }
3144
3145
        return $this->stubValues($values);
3146
    }
3147
3148
    protected function stubValues(array $fieldValues)
3149
    {
3150
        foreach ($fieldValues as &$languageValues) {
3151
            foreach ($languageValues as &$value) {
3152
                $value = new ValueStub($value);
3153
            }
3154
        }
3155
3156
        return $fieldValues;
3157
    }
3158
3159
    /**
3160
     * Asserts that calling updateContent() with given API field set causes calling
3161
     * Handler::updateContent() with given SPI field set.
3162
     *
3163
     * @param string $initialLanguageCode
3164
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3165
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
3166
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $existingFields
3167
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3168
     * @param bool $execute
3169
     *
3170
     * @return mixed
3171
     */
3172
    protected function assertForTestUpdateContentNonRedundantFieldSet(
3173
        $initialLanguageCode,
3174
        array $structFields,
3175
        array $spiFields,
3176
        array $existingFields,
3177
        array $fieldDefinitions,
3178
        $execute = true
3179
    ) {
3180
        $repositoryMock = $this->getRepositoryMock();
3181
        $permissionResolverMock = $this->getPermissionResolverMock();
3182
        $permissionResolverMock
3183
            ->expects($this->once())
3184
            ->method('getCurrentUserReference')
3185
            ->willReturn(new UserReference(169));
3186
        $mockedService = $this->getPartlyMockedContentService(['loadContent', 'loadRelations'], $permissionResolverMock);
3187
        $permissionResolverMock = $this->getPermissionResolverMock();
3188
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
3189
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
3190
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
3191
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
3192
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
3193
        $domainMapperMock = $this->getDomainMapperMock();
3194
        $relationProcessorMock = $this->getRelationProcessorMock();
3195
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
3196
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
3197
        $existingLanguageCodes = array_map(
3198
            function (Field $field) {
3199
                return $field->languageCode;
3200
            },
3201
            $existingFields
3202
        );
3203
        $languageCodes = $this->determineLanguageCodesForUpdate(
3204
            $initialLanguageCode,
3205
            $structFields,
3206
            $existingLanguageCodes
3207
        );
3208
        $versionInfo = new VersionInfo(
3209
            [
3210
                'contentInfo' => new ContentInfo(
3211
                    [
3212
                        'id' => 42,
3213
                        'contentTypeId' => 24,
3214
                        'mainLanguageCode' => 'eng-GB',
3215
                    ]
3216
                ),
3217
                'versionNo' => 7,
3218
                'languageCodes' => $existingLanguageCodes,
3219
                'status' => VersionInfo::STATUS_DRAFT,
3220
            ]
3221
        );
3222
        $content = new Content(
3223
            [
3224
                'versionInfo' => $versionInfo,
3225
                'internalFields' => $existingFields,
3226
            ]
3227
        );
3228
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
3229
3230
        $languageHandlerMock->expects($this->any())
3231
            ->method('loadByLanguageCode')
3232
            ->with($this->isType('string'))
3233
            ->will(
3234
                $this->returnCallback(
3235
                    function () {
3236
                        return new Language(['id' => 4242]);
3237
                    }
3238
                )
3239
            );
3240
3241
        $mockedService->expects($this->once())
3242
            ->method('loadContent')
3243
            ->with(
3244
                $this->equalTo(42),
3245
                $this->equalTo(null),
3246
                $this->equalTo(7)
3247
            )->will(
3248
                $this->returnValue($content)
3249
            );
3250
3251
        $repositoryMock->expects($this->once())->method('beginTransaction');
3252
3253
        $permissionResolverMock->expects($this->once())
3254
            ->method('canUser')
3255
            ->with(
3256
                $this->equalTo('content'),
3257
                $this->equalTo('edit'),
3258
                $this->equalTo($content),
3259
                $this->isType('array')
3260
            )->will($this->returnValue(true));
3261
3262
        $contentTypeServiceMock->expects($this->once())
3263
            ->method('loadContentType')
3264
            ->with($this->equalTo(24))
3265
            ->will($this->returnValue($contentType));
3266
3267
        $repositoryMock->expects($this->once())
3268
            ->method('getContentTypeService')
3269
            ->will($this->returnValue($contentTypeServiceMock));
3270
3271
        $fieldTypeMock->expects($this->any())
3272
            ->method('acceptValue')
3273
            ->will(
3274
                $this->returnCallback(
3275
                    function ($valueString) {
3276
                        return new ValueStub($valueString);
3277
                    }
3278
                )
3279
            );
3280
3281
        $emptyValue = self::EMPTY_FIELD_VALUE;
3282
        $fieldTypeMock->expects($this->any())
3283
            ->method('toPersistenceValue')
3284
            ->will(
3285
                $this->returnCallback(
3286
                    function (ValueStub $value) {
3287
                        return (string)$value;
3288
                    }
3289
                )
3290
            );
3291
3292
        $fieldTypeMock->expects($this->any())
3293
            ->method('isEmptyValue')
3294
            ->will(
3295
                $this->returnCallback(
3296
                    function (ValueStub $value) use ($emptyValue) {
3297
                        return $emptyValue === (string)$value;
3298
                    }
3299
                )
3300
            );
3301
3302
        $fieldTypeMock->expects($this->any())
3303
            ->method('validate')
3304
            ->will($this->returnValue([]));
3305
3306
        $this->getFieldTypeRegistryMock()->expects($this->any())
3307
            ->method('getFieldType')
3308
            ->will($this->returnValue($fieldTypeMock));
3309
3310
        $relationProcessorMock
3311
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
3312
            ->method('appendFieldRelations')
3313
            ->with(
3314
                $this->isType('array'),
3315
                $this->isType('array'),
3316
                $this->isInstanceOf(SPIFieldType::class),
3317
                $this->isInstanceOf(Value::class),
3318
                $this->anything()
3319
            );
3320
3321
        $values = $this->determineValuesForUpdate(
3322
            $initialLanguageCode,
3323
            $structFields,
3324
            $content,
3325
            $fieldDefinitions,
3326
            $languageCodes
3327
        );
3328
        $nameSchemaServiceMock->expects($this->once())
3329
            ->method('resolveNameSchema')
3330
            ->with(
3331
                $this->equalTo($content),
3332
                $this->equalTo($values),
3333
                $this->equalTo($languageCodes)
3334
            )->will($this->returnValue([]));
3335
3336
        $existingRelations = ['RELATIONS!!!'];
3337
        $mockedService->expects($this->once())
3338
            ->method('loadRelations')
3339
            ->with($content->versionInfo)
3340
            ->will($this->returnValue($existingRelations));
3341
        $relationProcessorMock->expects($this->any())
3342
            ->method('processFieldRelations')
3343
            ->with(
3344
                $this->isType('array'),
3345
                $this->equalTo(42),
3346
                $this->isType('int'),
3347
                $this->equalTo($contentType),
3348
                $this->equalTo($existingRelations)
3349
            );
3350
3351
        $contentUpdateStruct = new ContentUpdateStruct(
3352
            [
3353
                'fields' => $structFields,
3354
                'initialLanguageCode' => $initialLanguageCode,
3355
            ]
3356
        );
3357
3358
        if ($execute) {
3359
            $spiContentUpdateStruct = new SPIContentUpdateStruct(
3360
                [
3361
                    'creatorId' => 169,
3362
                    'fields' => $spiFields,
3363
                    'modificationDate' => time(),
3364
                    'initialLanguageId' => 4242,
3365
                ]
3366
            );
3367
3368
            // During code coverage runs, timestamp might differ 1-3 seconds
3369
            $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct;
3370
            ++$spiContentUpdateStructTs1->modificationDate;
3371
3372
            $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1;
3373
            ++$spiContentUpdateStructTs2->modificationDate;
3374
3375
            $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2;
3376
            ++$spiContentUpdateStructTs3->modificationDate;
3377
3378
            $spiContent = new SPIContent(
3379
                [
3380
                    'versionInfo' => new SPIContent\VersionInfo(
3381
                        [
3382
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
3383
                            'versionNo' => 7,
3384
                        ]
3385
                    ),
3386
                ]
3387
            );
3388
3389
            $contentHandlerMock->expects($this->once())
3390
                ->method('updateContent')
3391
                ->with(
3392
                    42,
3393
                    7,
3394
                    $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3)
3395
                )
3396
                ->will($this->returnValue($spiContent));
3397
3398
            $repositoryMock->expects($this->once())->method('commit');
3399
            $domainMapperMock->expects($this->once())
3400
                ->method('buildContentDomainObject')
3401
                ->with(
3402
                    $this->isInstanceOf(SPIContent::class),
3403
                    $this->isInstanceOf(APIContentType::class)
3404
                )
3405
                ->willReturn($this->createMock(APIContent::class));
3406
3407
            $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
3408
        }
3409
3410
        return [$content->versionInfo, $contentUpdateStruct];
3411
    }
3412
3413
    public function providerForTestUpdateContentNonRedundantFieldSet1()
3414
    {
3415
        $spiFields = [
3416
            new SPIField(
3417
                [
3418
                    'id' => '100',
3419
                    'fieldDefinitionId' => 'fieldDefinitionId',
3420
                    'type' => 'fieldTypeIdentifier',
3421
                    'value' => 'newValue',
3422
                    'languageCode' => 'eng-GB',
3423
                    'versionNo' => 7,
3424
                ]
3425
            ),
3426
        ];
3427
3428
        return [
3429
            // With languages set
3430
            [
3431
                'eng-GB',
3432
                [
3433
                    new Field(
3434
                        [
3435
                            'fieldDefIdentifier' => 'identifier',
3436
                            'value' => 'newValue',
3437
                            'languageCode' => 'eng-GB',
3438
                        ]
3439
                    ),
3440
                ],
3441
                $spiFields,
3442
            ],
3443
            // Without languages set
3444
            [
3445
                null,
3446
                [
3447
                    new Field(
3448
                        [
3449
                            'fieldDefIdentifier' => 'identifier',
3450
                            'value' => 'newValue',
3451
                            'languageCode' => null,
3452
                        ]
3453
                    ),
3454
                ],
3455
                $spiFields,
3456
            ],
3457
            // Adding new language without fields
3458
            [
3459
                'eng-US',
3460
                [],
3461
                [],
3462
            ],
3463
        ];
3464
    }
3465
3466
    /**
3467
     * Test for the updateContent() method.
3468
     *
3469
     * Testing the simplest use case.
3470
     *
3471
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3472
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3473
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3474
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1
3475
     */
3476
    public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields)
3477
    {
3478
        $existingFields = [
3479
            new Field(
3480
                [
3481
                    'id' => '100',
3482
                    'fieldDefIdentifier' => 'identifier',
3483
                    'value' => 'initialValue',
3484
                    'languageCode' => 'eng-GB',
3485
                ]
3486
            ),
3487
        ];
3488
3489
        $fieldDefinitions = [
3490
            new FieldDefinition(
3491
                [
3492
                    'id' => 'fieldDefinitionId',
3493
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3494
                    'isTranslatable' => false,
3495
                    'identifier' => 'identifier',
3496
                    'isRequired' => false,
3497
                    'defaultValue' => 'defaultValue',
3498
                ]
3499
            ),
3500
        ];
3501
3502
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3503
            $initialLanguageCode,
3504
            $structFields,
3505
            $spiFields,
3506
            $existingFields,
3507
            $fieldDefinitions
3508
        );
3509
    }
3510
3511
    public function providerForTestUpdateContentNonRedundantFieldSet2()
3512
    {
3513
        $spiFields0 = [
3514
            new SPIField(
3515
                [
3516
                    'id' => '100',
3517
                    'fieldDefinitionId' => 'fieldDefinitionId',
3518
                    'type' => 'fieldTypeIdentifier',
3519
                    'value' => 'newValue',
3520
                    'languageCode' => 'eng-GB',
3521
                    'versionNo' => 7,
3522
                ]
3523
            ),
3524
        ];
3525
        $spiFields1 = [
3526
            new SPIField(
3527
                [
3528
                    'id' => null,
3529
                    'fieldDefinitionId' => 'fieldDefinitionId',
3530
                    'type' => 'fieldTypeIdentifier',
3531
                    'value' => 'newValue',
3532
                    'languageCode' => 'eng-US',
3533
                    'versionNo' => 7,
3534
                ]
3535
            ),
3536
        ];
3537
        $spiFields2 = [
3538
            new SPIField(
3539
                [
3540
                    'id' => 100,
3541
                    'fieldDefinitionId' => 'fieldDefinitionId',
3542
                    'type' => 'fieldTypeIdentifier',
3543
                    'value' => 'newValue2',
3544
                    'languageCode' => 'eng-GB',
3545
                    'versionNo' => 7,
3546
                ]
3547
            ),
3548
            new SPIField(
3549
                [
3550
                    'id' => null,
3551
                    'fieldDefinitionId' => 'fieldDefinitionId',
3552
                    'type' => 'fieldTypeIdentifier',
3553
                    'value' => 'newValue1',
3554
                    'languageCode' => 'eng-US',
3555
                    'versionNo' => 7,
3556
                ]
3557
            ),
3558
        ];
3559
3560
        return [
3561
            // 0. With languages set
3562
            [
3563
                'eng-GB',
3564
                [
3565
                    new Field(
3566
                        [
3567
                            'fieldDefIdentifier' => 'identifier',
3568
                            'value' => 'newValue',
3569
                            'languageCode' => 'eng-GB',
3570
                        ]
3571
                    ),
3572
                ],
3573
                $spiFields0,
3574
            ],
3575
            // 1. Without languages set
3576
            [
3577
                null,
3578
                [
3579
                    new Field(
3580
                        [
3581
                            'fieldDefIdentifier' => 'identifier',
3582
                            'value' => 'newValue',
3583
                            'languageCode' => null,
3584
                        ]
3585
                    ),
3586
                ],
3587
                $spiFields0,
3588
            ],
3589
            // 2. New language with language set
3590
            [
3591
                'eng-GB',
3592
                [
3593
                    new Field(
3594
                        [
3595
                            'fieldDefIdentifier' => 'identifier',
3596
                            'value' => 'newValue',
3597
                            'languageCode' => 'eng-US',
3598
                        ]
3599
                    ),
3600
                ],
3601
                $spiFields1,
3602
            ],
3603
            // 3. New language without language set
3604
            [
3605
                'eng-US',
3606
                [
3607
                    new Field(
3608
                        [
3609
                            'fieldDefIdentifier' => 'identifier',
3610
                            'value' => 'newValue',
3611
                            'languageCode' => null,
3612
                        ]
3613
                    ),
3614
                ],
3615
                $spiFields1,
3616
            ],
3617
            // 4. New language and existing language with language set
3618
            [
3619
                'eng-GB',
3620
                [
3621
                    new Field(
3622
                        [
3623
                            'fieldDefIdentifier' => 'identifier',
3624
                            'value' => 'newValue1',
3625
                            'languageCode' => 'eng-US',
3626
                        ]
3627
                    ),
3628
                    new Field(
3629
                        [
3630
                            'fieldDefIdentifier' => 'identifier',
3631
                            'value' => 'newValue2',
3632
                            'languageCode' => 'eng-GB',
3633
                        ]
3634
                    ),
3635
                ],
3636
                $spiFields2,
3637
            ],
3638
            // 5. New language and existing language without language set
3639
            [
3640
                'eng-US',
3641
                [
3642
                    new Field(
3643
                        [
3644
                            'fieldDefIdentifier' => 'identifier',
3645
                            'value' => 'newValue1',
3646
                            'languageCode' => null,
3647
                        ]
3648
                    ),
3649
                    new Field(
3650
                        [
3651
                            'fieldDefIdentifier' => 'identifier',
3652
                            'value' => 'newValue2',
3653
                            'languageCode' => 'eng-GB',
3654
                        ]
3655
                    ),
3656
                ],
3657
                $spiFields2,
3658
            ],
3659
            // 6. Adding new language without fields
3660
            [
3661
                'eng-US',
3662
                [],
3663
                [
3664
                    new SPIField(
3665
                        [
3666
                            'id' => null,
3667
                            'fieldDefinitionId' => 'fieldDefinitionId',
3668
                            'type' => 'fieldTypeIdentifier',
3669
                            'value' => 'defaultValue',
3670
                            'languageCode' => 'eng-US',
3671
                            'versionNo' => 7,
3672
                        ]
3673
                    ),
3674
                ],
3675
            ],
3676
        ];
3677
    }
3678
3679
    /**
3680
     * Test for the updateContent() method.
3681
     *
3682
     * Testing with translatable field.
3683
     *
3684
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3685
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3686
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3687
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2
3688
     */
3689
    public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields)
3690
    {
3691
        $existingFields = [
3692
            new Field(
3693
                [
3694
                    'id' => '100',
3695
                    'fieldDefIdentifier' => 'identifier',
3696
                    'value' => 'initialValue',
3697
                    'languageCode' => 'eng-GB',
3698
                ]
3699
            ),
3700
        ];
3701
3702
        $fieldDefinitions = [
3703
            new FieldDefinition(
3704
                [
3705
                    'id' => 'fieldDefinitionId',
3706
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3707
                    'isTranslatable' => true,
3708
                    'identifier' => 'identifier',
3709
                    'isRequired' => false,
3710
                    'defaultValue' => 'defaultValue',
3711
                ]
3712
            ),
3713
        ];
3714
3715
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3716
            $initialLanguageCode,
3717
            $structFields,
3718
            $spiFields,
3719
            $existingFields,
3720
            $fieldDefinitions
3721
        );
3722
    }
3723
3724
    public function providerForTestUpdateContentNonRedundantFieldSet3()
3725
    {
3726
        $spiFields0 = [
3727
            new SPIField(
3728
                [
3729
                    'id' => null,
3730
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3731
                    'type' => 'fieldTypeIdentifier',
3732
                    'value' => 'newValue1',
3733
                    'languageCode' => 'eng-US',
3734
                    'versionNo' => 7,
3735
                ]
3736
            ),
3737
        ];
3738
        $spiFields1 = [
3739
            new SPIField(
3740
                [
3741
                    'id' => 100,
3742
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3743
                    'type' => 'fieldTypeIdentifier',
3744
                    'value' => 'newValue2',
3745
                    'languageCode' => 'eng-GB',
3746
                    'versionNo' => 7,
3747
                ]
3748
            ),
3749
            new SPIField(
3750
                [
3751
                    'id' => null,
3752
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3753
                    'type' => 'fieldTypeIdentifier',
3754
                    'value' => 'newValue1',
3755
                    'languageCode' => 'eng-US',
3756
                    'versionNo' => 7,
3757
                ]
3758
            ),
3759
        ];
3760
        $spiFields2 = [
3761
            new SPIField(
3762
                [
3763
                    'id' => 100,
3764
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3765
                    'type' => 'fieldTypeIdentifier',
3766
                    'value' => 'newValue2',
3767
                    'languageCode' => 'eng-GB',
3768
                    'versionNo' => 7,
3769
                ]
3770
            ),
3771
            new SPIField(
3772
                [
3773
                    'id' => null,
3774
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3775
                    'type' => 'fieldTypeIdentifier',
3776
                    'value' => 'newValue1',
3777
                    'languageCode' => 'eng-US',
3778
                    'versionNo' => 7,
3779
                ]
3780
            ),
3781
            new SPIField(
3782
                [
3783
                    'id' => 101,
3784
                    'fieldDefinitionId' => 'fieldDefinitionId2',
3785
                    'type' => 'fieldTypeIdentifier',
3786
                    'value' => 'newValue3',
3787
                    'languageCode' => 'eng-GB',
3788
                    'versionNo' => 7,
3789
                ]
3790
            ),
3791
        ];
3792
        $spiFields3 = [
3793
            new SPIField(
3794
                [
3795
                    'id' => null,
3796
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3797
                    'type' => 'fieldTypeIdentifier',
3798
                    'value' => 'defaultValue1',
3799
                    'languageCode' => 'eng-US',
3800
                    'versionNo' => 7,
3801
                ]
3802
            ),
3803
        ];
3804
3805
        return [
3806
            // 0. ew language with language set
3807
            [
3808
                'eng-US',
3809
                [
3810
                    new Field(
3811
                        [
3812
                            'fieldDefIdentifier' => 'identifier1',
3813
                            'value' => 'newValue1',
3814
                            'languageCode' => 'eng-US',
3815
                        ]
3816
                    ),
3817
                ],
3818
                $spiFields0,
3819
            ],
3820
            // 1. New language without language set
3821
            [
3822
                'eng-US',
3823
                [
3824
                    new Field(
3825
                        [
3826
                            'fieldDefIdentifier' => 'identifier1',
3827
                            'value' => 'newValue1',
3828
                            'languageCode' => null,
3829
                        ]
3830
                    ),
3831
                ],
3832
                $spiFields0,
3833
            ],
3834
            // 2. New language and existing language with language set
3835
            [
3836
                'eng-US',
3837
                [
3838
                    new Field(
3839
                        [
3840
                            'fieldDefIdentifier' => 'identifier1',
3841
                            'value' => 'newValue1',
3842
                            'languageCode' => 'eng-US',
3843
                        ]
3844
                    ),
3845
                    new Field(
3846
                        [
3847
                            'fieldDefIdentifier' => 'identifier1',
3848
                            'value' => 'newValue2',
3849
                            'languageCode' => 'eng-GB',
3850
                        ]
3851
                    ),
3852
                ],
3853
                $spiFields1,
3854
            ],
3855
            // 3. New language and existing language without language set
3856
            [
3857
                'eng-US',
3858
                [
3859
                    new Field(
3860
                        [
3861
                            'fieldDefIdentifier' => 'identifier1',
3862
                            'value' => 'newValue1',
3863
                            'languageCode' => null,
3864
                        ]
3865
                    ),
3866
                    new Field(
3867
                        [
3868
                            'fieldDefIdentifier' => 'identifier1',
3869
                            'value' => 'newValue2',
3870
                            'languageCode' => 'eng-GB',
3871
                        ]
3872
                    ),
3873
                ],
3874
                $spiFields1,
3875
            ],
3876
            // 4. New language and existing language with untranslatable field, with language set
3877
            [
3878
                'eng-US',
3879
                [
3880
                    new Field(
3881
                        [
3882
                            'fieldDefIdentifier' => 'identifier1',
3883
                            'value' => 'newValue1',
3884
                            'languageCode' => 'eng-US',
3885
                        ]
3886
                    ),
3887
                    new Field(
3888
                        [
3889
                            'fieldDefIdentifier' => 'identifier1',
3890
                            'value' => 'newValue2',
3891
                            'languageCode' => 'eng-GB',
3892
                        ]
3893
                    ),
3894
                    new Field(
3895
                        [
3896
                            'fieldDefIdentifier' => 'identifier2',
3897
                            'value' => 'newValue3',
3898
                            'languageCode' => 'eng-GB',
3899
                        ]
3900
                    ),
3901
                ],
3902
                $spiFields2,
3903
            ],
3904
            // 5. New language and existing language with untranslatable field, without language set
3905
            [
3906
                'eng-US',
3907
                [
3908
                    new Field(
3909
                        [
3910
                            'fieldDefIdentifier' => 'identifier1',
3911
                            'value' => 'newValue1',
3912
                            'languageCode' => null,
3913
                        ]
3914
                    ),
3915
                    new Field(
3916
                        [
3917
                            'fieldDefIdentifier' => 'identifier1',
3918
                            'value' => 'newValue2',
3919
                            'languageCode' => 'eng-GB',
3920
                        ]
3921
                    ),
3922
                    new Field(
3923
                        [
3924
                            'fieldDefIdentifier' => 'identifier2',
3925
                            'value' => 'newValue3',
3926
                            'languageCode' => null,
3927
                        ]
3928
                    ),
3929
                ],
3930
                $spiFields2,
3931
            ],
3932
            // 6. Adding new language without fields
3933
            [
3934
                'eng-US',
3935
                [],
3936
                $spiFields3,
3937
            ],
3938
        ];
3939
    }
3940
3941
    /**
3942
     * Test for the updateContent() method.
3943
     *
3944
     * Testing with new language and untranslatable field.
3945
     *
3946
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3947
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3948
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3949
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3
3950
     */
3951
    public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields)
3952
    {
3953
        $existingFields = [
3954
            new Field(
3955
                [
3956
                    'id' => '100',
3957
                    'fieldDefIdentifier' => 'identifier1',
3958
                    'value' => 'initialValue1',
3959
                    'languageCode' => 'eng-GB',
3960
                ]
3961
            ),
3962
            new Field(
3963
                [
3964
                    'id' => '101',
3965
                    'fieldDefIdentifier' => 'identifier2',
3966
                    'value' => 'initialValue2',
3967
                    'languageCode' => 'eng-GB',
3968
                ]
3969
            ),
3970
        ];
3971
3972
        $fieldDefinitions = [
3973
            new FieldDefinition(
3974
                [
3975
                    'id' => 'fieldDefinitionId1',
3976
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3977
                    'isTranslatable' => true,
3978
                    'identifier' => 'identifier1',
3979
                    'isRequired' => false,
3980
                    'defaultValue' => 'defaultValue1',
3981
                ]
3982
            ),
3983
            new FieldDefinition(
3984
                [
3985
                    'id' => 'fieldDefinitionId2',
3986
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3987
                    'isTranslatable' => false,
3988
                    'identifier' => 'identifier2',
3989
                    'isRequired' => false,
3990
                    'defaultValue' => 'defaultValue2',
3991
                ]
3992
            ),
3993
        ];
3994
3995
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3996
            $initialLanguageCode,
3997
            $structFields,
3998
            $spiFields,
3999
            $existingFields,
4000
            $fieldDefinitions
4001
        );
4002
    }
4003
4004
    public function providerForTestUpdateContentNonRedundantFieldSet4()
4005
    {
4006
        $spiFields0 = [
4007
            new SPIField(
4008
                [
4009
                    'id' => null,
4010
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4011
                    'type' => 'fieldTypeIdentifier',
4012
                    'value' => 'newValue1',
4013
                    'languageCode' => 'eng-US',
4014
                    'versionNo' => 7,
4015
                ]
4016
            ),
4017
        ];
4018
        $spiFields1 = [
4019
            new SPIField(
4020
                [
4021
                    'id' => 100,
4022
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4023
                    'type' => 'fieldTypeIdentifier',
4024
                    'value' => self::EMPTY_FIELD_VALUE,
4025
                    'languageCode' => 'eng-GB',
4026
                    'versionNo' => 7,
4027
                ]
4028
            ),
4029
            new SPIField(
4030
                [
4031
                    'id' => null,
4032
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4033
                    'type' => 'fieldTypeIdentifier',
4034
                    'value' => 'newValue1',
4035
                    'languageCode' => 'eng-US',
4036
                    'versionNo' => 7,
4037
                ]
4038
            ),
4039
        ];
4040
        $spiFields2 = [
4041
            new SPIField(
4042
                [
4043
                    'id' => 100,
4044
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4045
                    'type' => 'fieldTypeIdentifier',
4046
                    'value' => self::EMPTY_FIELD_VALUE,
4047
                    'languageCode' => 'eng-GB',
4048
                    'versionNo' => 7,
4049
                ]
4050
            ),
4051
        ];
4052
4053
        return [
4054
            // 0. New translation with empty field by default
4055
            [
4056
                'eng-US',
4057
                [
4058
                    new Field(
4059
                        [
4060
                            'fieldDefIdentifier' => 'identifier1',
4061
                            'value' => 'newValue1',
4062
                            'languageCode' => 'eng-US',
4063
                        ]
4064
                    ),
4065
                ],
4066
                $spiFields0,
4067
            ],
4068
            // 1. New translation with empty field by default, without language set
4069
            [
4070
                'eng-US',
4071
                [
4072
                    new Field(
4073
                        [
4074
                            'fieldDefIdentifier' => 'identifier1',
4075
                            'value' => 'newValue1',
4076
                            'languageCode' => null,
4077
                        ]
4078
                    ),
4079
                ],
4080
                $spiFields0,
4081
            ],
4082
            // 2. New translation with empty field given
4083
            [
4084
                'eng-US',
4085
                [
4086
                    new Field(
4087
                        [
4088
                            'fieldDefIdentifier' => 'identifier1',
4089
                            'value' => 'newValue1',
4090
                            'languageCode' => 'eng-US',
4091
                        ]
4092
                    ),
4093
                    new Field(
4094
                        [
4095
                            'fieldDefIdentifier' => 'identifier2',
4096
                            'value' => self::EMPTY_FIELD_VALUE,
4097
                            'languageCode' => 'eng-US',
4098
                        ]
4099
                    ),
4100
                ],
4101
                $spiFields0,
4102
            ],
4103
            // 3. New translation with empty field given, without language set
4104
            [
4105
                'eng-US',
4106
                [
4107
                    new Field(
4108
                        [
4109
                            'fieldDefIdentifier' => 'identifier1',
4110
                            'value' => 'newValue1',
4111
                            'languageCode' => null,
4112
                        ]
4113
                    ),
4114
                    new Field(
4115
                        [
4116
                            'fieldDefIdentifier' => 'identifier2',
4117
                            'value' => self::EMPTY_FIELD_VALUE,
4118
                            'languageCode' => null,
4119
                        ]
4120
                    ),
4121
                ],
4122
                $spiFields0,
4123
            ],
4124
            // 4. Updating existing language with empty value
4125
            [
4126
                'eng-US',
4127
                [
4128
                    new Field(
4129
                        [
4130
                            'fieldDefIdentifier' => 'identifier1',
4131
                            'value' => 'newValue1',
4132
                            'languageCode' => 'eng-US',
4133
                        ]
4134
                    ),
4135
                    new Field(
4136
                        [
4137
                            'fieldDefIdentifier' => 'identifier1',
4138
                            'value' => self::EMPTY_FIELD_VALUE,
4139
                            'languageCode' => 'eng-GB',
4140
                        ]
4141
                    ),
4142
                ],
4143
                $spiFields1,
4144
            ],
4145
            // 5. Updating existing language with empty value, without language set
4146
            [
4147
                'eng-US',
4148
                [
4149
                    new Field(
4150
                        [
4151
                            'fieldDefIdentifier' => 'identifier1',
4152
                            'value' => 'newValue1',
4153
                            'languageCode' => null,
4154
                        ]
4155
                    ),
4156
                    new Field(
4157
                        [
4158
                            'fieldDefIdentifier' => 'identifier1',
4159
                            'value' => self::EMPTY_FIELD_VALUE,
4160
                            'languageCode' => 'eng-GB',
4161
                        ]
4162
                    ),
4163
                ],
4164
                $spiFields1,
4165
            ],
4166
            // 6. Updating existing language with empty value and adding new language with empty value
4167
            [
4168
                'eng-US',
4169
                [
4170
                    new Field(
4171
                        [
4172
                            'fieldDefIdentifier' => 'identifier1',
4173
                            'value' => self::EMPTY_FIELD_VALUE,
4174
                            'languageCode' => 'eng-US',
4175
                        ]
4176
                    ),
4177
                    new Field(
4178
                        [
4179
                            'fieldDefIdentifier' => 'identifier1',
4180
                            'value' => self::EMPTY_FIELD_VALUE,
4181
                            'languageCode' => 'eng-GB',
4182
                        ]
4183
                    ),
4184
                ],
4185
                $spiFields2,
4186
            ],
4187
            // 7. Updating existing language with empty value and adding new language with empty value,
4188
            // without language set
4189
            [
4190
                'eng-US',
4191
                [
4192
                    new Field(
4193
                        [
4194
                            'fieldDefIdentifier' => 'identifier1',
4195
                            'value' => self::EMPTY_FIELD_VALUE,
4196
                            'languageCode' => null,
4197
                        ]
4198
                    ),
4199
                    new Field(
4200
                        [
4201
                            'fieldDefIdentifier' => 'identifier1',
4202
                            'value' => self::EMPTY_FIELD_VALUE,
4203
                            'languageCode' => 'eng-GB',
4204
                        ]
4205
                    ),
4206
                ],
4207
                $spiFields2,
4208
            ],
4209
            // 8. Adding new language with no fields given
4210
            [
4211
                'eng-US',
4212
                [],
4213
                [],
4214
            ],
4215
            // 9. Adding new language with fields
4216
            [
4217
                'eng-US',
4218
                [
4219
                    new Field(
4220
                        [
4221
                            'fieldDefIdentifier' => 'identifier1',
4222
                            'value' => self::EMPTY_FIELD_VALUE,
4223
                            'languageCode' => 'eng-US',
4224
                        ]
4225
                    ),
4226
                ],
4227
                [],
4228
            ],
4229
            // 10. Adding new language with fields, without language set
4230
            [
4231
                'eng-US',
4232
                [
4233
                    new Field(
4234
                        [
4235
                            'fieldDefIdentifier' => 'identifier1',
4236
                            'value' => self::EMPTY_FIELD_VALUE,
4237
                            'languageCode' => null,
4238
                        ]
4239
                    ),
4240
                ],
4241
                [],
4242
            ],
4243
        ];
4244
    }
4245
4246
    /**
4247
     * Test for the updateContent() method.
4248
     *
4249
     * Testing with empty values.
4250
     *
4251
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4252
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4253
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4254
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4
4255
     */
4256
    public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields)
4257
    {
4258
        $existingFields = [
4259
            new Field(
4260
                [
4261
                    'id' => '100',
4262
                    'fieldDefIdentifier' => 'identifier1',
4263
                    'value' => 'initialValue1',
4264
                    'languageCode' => 'eng-GB',
4265
                ]
4266
            ),
4267
            new Field(
4268
                [
4269
                    'id' => '101',
4270
                    'fieldDefIdentifier' => 'identifier2',
4271
                    'value' => 'initialValue2',
4272
                    'languageCode' => 'eng-GB',
4273
                ]
4274
            ),
4275
        ];
4276
4277
        $fieldDefinitions = [
4278
            new FieldDefinition(
4279
                [
4280
                    'id' => 'fieldDefinitionId1',
4281
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4282
                    'isTranslatable' => true,
4283
                    'identifier' => 'identifier1',
4284
                    'isRequired' => false,
4285
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4286
                ]
4287
            ),
4288
            new FieldDefinition(
4289
                [
4290
                    'id' => 'fieldDefinitionId2',
4291
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4292
                    'isTranslatable' => true,
4293
                    'identifier' => 'identifier2',
4294
                    'isRequired' => false,
4295
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4296
                ]
4297
            ),
4298
        ];
4299
4300
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4301
            $initialLanguageCode,
4302
            $structFields,
4303
            $spiFields,
4304
            $existingFields,
4305
            $fieldDefinitions
4306
        );
4307
    }
4308
4309
    /**
4310
     * @todo add first field empty
4311
     *
4312
     * @return array
4313
     */
4314
    public function providerForTestUpdateContentNonRedundantFieldSetComplex()
4315
    {
4316
        $spiFields0 = [
4317
            new SPIField(
4318
                [
4319
                    'id' => 100,
4320
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4321
                    'type' => 'fieldTypeIdentifier',
4322
                    'value' => 'newValue1-eng-GB',
4323
                    'languageCode' => 'eng-GB',
4324
                    'versionNo' => 7,
4325
                ]
4326
            ),
4327
            new SPIField(
4328
                [
4329
                    'id' => null,
4330
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4331
                    'type' => 'fieldTypeIdentifier',
4332
                    'value' => 'newValue4',
4333
                    'languageCode' => 'eng-US',
4334
                    'versionNo' => 7,
4335
                ]
4336
            ),
4337
        ];
4338
        $spiFields1 = [
4339
            new SPIField(
4340
                [
4341
                    'id' => 100,
4342
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4343
                    'type' => 'fieldTypeIdentifier',
4344
                    'value' => 'newValue1-eng-GB',
4345
                    'languageCode' => 'eng-GB',
4346
                    'versionNo' => 7,
4347
                ]
4348
            ),
4349
            new SPIField(
4350
                [
4351
                    'id' => null,
4352
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4353
                    'type' => 'fieldTypeIdentifier',
4354
                    'value' => 'newValue2',
4355
                    'languageCode' => 'eng-US',
4356
                    'versionNo' => 7,
4357
                ]
4358
            ),
4359
            new SPIField(
4360
                [
4361
                    'id' => null,
4362
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4363
                    'type' => 'fieldTypeIdentifier',
4364
                    'value' => 'defaultValue4',
4365
                    'languageCode' => 'eng-US',
4366
                    'versionNo' => 7,
4367
                ]
4368
            ),
4369
        ];
4370
        $spiFields2 = [
4371
            new SPIField(
4372
                [
4373
                    'id' => 100,
4374
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4375
                    'type' => 'fieldTypeIdentifier',
4376
                    'value' => 'newValue1-eng-GB',
4377
                    'languageCode' => 'eng-GB',
4378
                    'versionNo' => 7,
4379
                ]
4380
            ),
4381
            new SPIField(
4382
                [
4383
                    'id' => null,
4384
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4385
                    'type' => 'fieldTypeIdentifier',
4386
                    'value' => 'newValue2',
4387
                    'languageCode' => 'eng-US',
4388
                    'versionNo' => 7,
4389
                ]
4390
            ),
4391
            new SPIField(
4392
                [
4393
                    'id' => null,
4394
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4395
                    'type' => 'fieldTypeIdentifier',
4396
                    'value' => 'defaultValue4',
4397
                    'languageCode' => 'ger-DE',
4398
                    'versionNo' => 7,
4399
                ]
4400
            ),
4401
            new SPIField(
4402
                [
4403
                    'id' => null,
4404
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4405
                    'type' => 'fieldTypeIdentifier',
4406
                    'value' => 'defaultValue4',
4407
                    'languageCode' => 'eng-US',
4408
                    'versionNo' => 7,
4409
                ]
4410
            ),
4411
        ];
4412
4413
        return [
4414
            // 0. Add new language and update existing
4415
            [
4416
                'eng-US',
4417
                [
4418
                    new Field(
4419
                        [
4420
                            'fieldDefIdentifier' => 'identifier4',
4421
                            'value' => 'newValue4',
4422
                            'languageCode' => 'eng-US',
4423
                        ]
4424
                    ),
4425
                    new Field(
4426
                        [
4427
                            'fieldDefIdentifier' => 'identifier1',
4428
                            'value' => 'newValue1-eng-GB',
4429
                            'languageCode' => 'eng-GB',
4430
                        ]
4431
                    ),
4432
                ],
4433
                $spiFields0,
4434
            ],
4435
            // 1. Add new language and update existing, without language set
4436
            [
4437
                'eng-US',
4438
                [
4439
                    new Field(
4440
                        [
4441
                            'fieldDefIdentifier' => 'identifier4',
4442
                            'value' => 'newValue4',
4443
                            'languageCode' => null,
4444
                        ]
4445
                    ),
4446
                    new Field(
4447
                        [
4448
                            'fieldDefIdentifier' => 'identifier1',
4449
                            'value' => 'newValue1-eng-GB',
4450
                            'languageCode' => 'eng-GB',
4451
                        ]
4452
                    ),
4453
                ],
4454
                $spiFields0,
4455
            ],
4456
            // 2. Add new language and update existing variant
4457
            [
4458
                'eng-US',
4459
                [
4460
                    new Field(
4461
                        [
4462
                            'fieldDefIdentifier' => 'identifier2',
4463
                            'value' => 'newValue2',
4464
                            'languageCode' => 'eng-US',
4465
                        ]
4466
                    ),
4467
                    new Field(
4468
                        [
4469
                            'fieldDefIdentifier' => 'identifier1',
4470
                            'value' => 'newValue1-eng-GB',
4471
                            'languageCode' => 'eng-GB',
4472
                        ]
4473
                    ),
4474
                ],
4475
                $spiFields1,
4476
            ],
4477
            // 3. Add new language and update existing variant, without language set
4478
            [
4479
                'eng-US',
4480
                [
4481
                    new Field(
4482
                        [
4483
                            'fieldDefIdentifier' => 'identifier2',
4484
                            'value' => 'newValue2',
4485
                            'languageCode' => null,
4486
                        ]
4487
                    ),
4488
                    new Field(
4489
                        [
4490
                            'fieldDefIdentifier' => 'identifier1',
4491
                            'value' => 'newValue1-eng-GB',
4492
                            'languageCode' => 'eng-GB',
4493
                        ]
4494
                    ),
4495
                ],
4496
                $spiFields1,
4497
            ],
4498
            // 4. Update with multiple languages
4499
            [
4500
                'ger-DE',
4501
                [
4502
                    new Field(
4503
                        [
4504
                            'fieldDefIdentifier' => 'identifier2',
4505
                            'value' => 'newValue2',
4506
                            'languageCode' => 'eng-US',
4507
                        ]
4508
                    ),
4509
                    new Field(
4510
                        [
4511
                            'fieldDefIdentifier' => 'identifier1',
4512
                            'value' => 'newValue1-eng-GB',
4513
                            'languageCode' => 'eng-GB',
4514
                        ]
4515
                    ),
4516
                ],
4517
                $spiFields2,
4518
            ],
4519
            // 5. Update with multiple languages without language set
4520
            [
4521
                'ger-DE',
4522
                [
4523
                    new Field(
4524
                        [
4525
                            'fieldDefIdentifier' => 'identifier2',
4526
                            'value' => 'newValue2',
4527
                            'languageCode' => 'eng-US',
4528
                        ]
4529
                    ),
4530
                    new Field(
4531
                        [
4532
                            'fieldDefIdentifier' => 'identifier1',
4533
                            'value' => 'newValue1-eng-GB',
4534
                            'languageCode' => null,
4535
                        ]
4536
                    ),
4537
                ],
4538
                $spiFields2,
4539
            ],
4540
        ];
4541
    }
4542
4543
    protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex()
4544
    {
4545
        $existingFields = [
4546
            new Field(
4547
                [
4548
                    'id' => '100',
4549
                    'fieldDefIdentifier' => 'identifier1',
4550
                    'value' => 'initialValue1',
4551
                    'languageCode' => 'eng-GB',
4552
                ]
4553
            ),
4554
            new Field(
4555
                [
4556
                    'id' => '101',
4557
                    'fieldDefIdentifier' => 'identifier2',
4558
                    'value' => 'initialValue2',
4559
                    'languageCode' => 'eng-GB',
4560
                ]
4561
            ),
4562
            new Field(
4563
                [
4564
                    'id' => '102',
4565
                    'fieldDefIdentifier' => 'identifier3',
4566
                    'value' => 'initialValue3',
4567
                    'languageCode' => 'eng-GB',
4568
                ]
4569
            ),
4570
            new Field(
4571
                [
4572
                    'id' => '103',
4573
                    'fieldDefIdentifier' => 'identifier4',
4574
                    'value' => 'initialValue4',
4575
                    'languageCode' => 'eng-GB',
4576
                ]
4577
            ),
4578
        ];
4579
4580
        $fieldDefinitions = [
4581
            new FieldDefinition(
4582
                [
4583
                    'id' => 'fieldDefinitionId1',
4584
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4585
                    'isTranslatable' => false,
4586
                    'identifier' => 'identifier1',
4587
                    'isRequired' => false,
4588
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4589
                ]
4590
            ),
4591
            new FieldDefinition(
4592
                [
4593
                    'id' => 'fieldDefinitionId2',
4594
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4595
                    'isTranslatable' => true,
4596
                    'identifier' => 'identifier2',
4597
                    'isRequired' => false,
4598
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4599
                ]
4600
            ),
4601
            new FieldDefinition(
4602
                [
4603
                    'id' => 'fieldDefinitionId3',
4604
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4605
                    'isTranslatable' => false,
4606
                    'identifier' => 'identifier3',
4607
                    'isRequired' => false,
4608
                    'defaultValue' => 'defaultValue3',
4609
                ]
4610
            ),
4611
            new FieldDefinition(
4612
                [
4613
                    'id' => 'fieldDefinitionId4',
4614
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4615
                    'isTranslatable' => true,
4616
                    'identifier' => 'identifier4',
4617
                    'isRequired' => false,
4618
                    'defaultValue' => 'defaultValue4',
4619
                ]
4620
            ),
4621
        ];
4622
4623
        return [$existingFields, $fieldDefinitions];
4624
    }
4625
4626
    /**
4627
     * Test for the updateContent() method.
4628
     *
4629
     * Testing more complex cases.
4630
     *
4631
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4632
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4633
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4634
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex
4635
     */
4636
    public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields)
4637
    {
4638
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
4639
4640
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4641
            $initialLanguageCode,
4642
            $structFields,
4643
            $spiFields,
4644
            $existingFields,
4645
            $fieldDefinitions
4646
        );
4647
    }
4648
4649
    public function providerForTestUpdateContentWithInvalidLanguage()
4650
    {
4651
        return [
4652
            [
4653
                'eng-GB',
4654
                [
4655
                    new Field(
4656
                        [
4657
                            'fieldDefIdentifier' => 'identifier',
4658
                            'value' => 'newValue',
4659
                            'languageCode' => 'Klingon',
4660
                        ]
4661
                    ),
4662
                ],
4663
            ],
4664
            [
4665
                'Klingon',
4666
                [
4667
                    new Field(
4668
                        [
4669
                            'fieldDefIdentifier' => 'identifier',
4670
                            'value' => 'newValue',
4671
                            'languageCode' => 'eng-GB',
4672
                        ]
4673
                    ),
4674
                ],
4675
            ],
4676
        ];
4677
    }
4678
4679
    /**
4680
     * Test for the updateContent() method.
4681
     *
4682
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4683
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4684
     * @dataProvider providerForTestUpdateContentWithInvalidLanguage
4685
     */
4686
    public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields)
4687
    {
4688
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);
4689
        $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\'');
4690
4691
        $permissionResolverMock = $this->getPermissionResolverMock();
4692
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4693
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4694
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4695
        $versionInfo = new VersionInfo(
4696
            [
4697
                'contentInfo' => new ContentInfo(
4698
                    [
4699
                        'id' => 42,
4700
                        'contentTypeId' => 24,
4701
                        'mainLanguageCode' => 'eng-GB',
4702
                    ]
4703
                ),
4704
                'versionNo' => 7,
4705
                'languageCodes' => ['eng-GB'],
4706
                'status' => VersionInfo::STATUS_DRAFT,
4707
            ]
4708
        );
4709
        $content = new Content(
4710
            [
4711
                'versionInfo' => $versionInfo,
4712
                'internalFields' => [],
4713
            ]
4714
        );
4715
4716
        $languageHandlerMock->expects($this->any())
4717
            ->method('loadByLanguageCode')
4718
            ->with($this->isType('string'))
4719
            ->will(
4720
                $this->returnCallback(
4721
                    function ($languageCode) {
4722
                        if ($languageCode === 'Klingon') {
4723
                            throw new NotFoundException('Language', 'Klingon');
4724
                        }
4725
4726
                        return new Language(['id' => 4242]);
4727
                    }
4728
                )
4729
            );
4730
4731
        $mockedService->expects($this->once())
4732
            ->method('loadContent')
4733
            ->with(
4734
                $this->equalTo(42),
4735
                $this->equalTo(null),
4736
                $this->equalTo(7)
4737
            )->will(
4738
                $this->returnValue($content)
4739
            );
4740
4741
        $permissionResolverMock->expects($this->once())
4742
            ->method('canUser')
4743
            ->with(
4744
                $this->equalTo('content'),
4745
                $this->equalTo('edit'),
4746
                $this->equalTo($content),
4747
                $this->isType('array')
4748
            )->will($this->returnValue(true));
4749
4750
        $contentUpdateStruct = new ContentUpdateStruct(
4751
            [
4752
                'fields' => $structFields,
4753
                'initialLanguageCode' => $initialLanguageCode,
4754
            ]
4755
        );
4756
4757
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4758
    }
4759
4760
    protected function assertForUpdateContentContentValidationException(
4761
        $initialLanguageCode,
4762
        $structFields,
4763
        $fieldDefinitions = []
4764
    ) {
4765
        $repositoryMock = $this->getRepositoryMock();
4766
        $permissionResolverMock = $this->getPermissionResolverMock();
4767
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4768
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4769
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4770
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4771
        $versionInfo = new VersionInfo(
4772
            [
4773
                'contentInfo' => new ContentInfo(
4774
                    [
4775
                        'id' => 42,
4776
                        'contentTypeId' => 24,
4777
                        'mainLanguageCode' => 'eng-GB',
4778
                    ]
4779
                ),
4780
                'versionNo' => 7,
4781
                'languageCodes' => ['eng-GB'],
4782
                'status' => VersionInfo::STATUS_DRAFT,
4783
            ]
4784
        );
4785
        $content = new Content(
4786
            [
4787
                'versionInfo' => $versionInfo,
4788
                'internalFields' => [],
4789
            ]
4790
        );
4791
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4792
4793
        $languageHandlerMock->expects($this->any())
4794
            ->method('loadByLanguageCode')
4795
            ->with($this->isType('string'))
4796
            ->will(
4797
                $this->returnCallback(
4798
                    function ($languageCode) {
4799
                        if ($languageCode === 'Klingon') {
4800
                            throw new NotFoundException('Language', 'Klingon');
4801
                        }
4802
4803
                        return new Language(['id' => 4242]);
4804
                    }
4805
                )
4806
            );
4807
4808
        $mockedService->expects($this->once())
4809
            ->method('loadContent')
4810
            ->with(
4811
                $this->equalTo(42),
4812
                $this->equalTo(null),
4813
                $this->equalTo(7)
4814
            )->will(
4815
                $this->returnValue($content)
4816
            );
4817
4818
        $permissionResolverMock->expects($this->once())
4819
            ->method('canUser')
4820
            ->with(
4821
                $this->equalTo('content'),
4822
                $this->equalTo('edit'),
4823
                $this->equalTo($content),
4824
                $this->isType('array')
4825
            )->will($this->returnValue(true));
4826
4827
        $contentTypeServiceMock->expects($this->once())
4828
            ->method('loadContentType')
4829
            ->with($this->equalTo(24))
4830
            ->will($this->returnValue($contentType));
4831
4832
        $repositoryMock->expects($this->once())
4833
            ->method('getContentTypeService')
4834
            ->will($this->returnValue($contentTypeServiceMock));
4835
4836
        $contentUpdateStruct = new ContentUpdateStruct(
4837
            [
4838
                'fields' => $structFields,
4839
                'initialLanguageCode' => $initialLanguageCode,
4840
            ]
4841
        );
4842
4843
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4844
    }
4845
4846
    public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition()
4847
    {
4848
        return [
4849
            [
4850
                'eng-GB',
4851
                [
4852
                    new Field(
4853
                        [
4854
                            'fieldDefIdentifier' => 'identifier',
4855
                            'value' => 'newValue',
4856
                            'languageCode' => 'eng-GB',
4857
                        ]
4858
                    ),
4859
                ],
4860
            ],
4861
        ];
4862
    }
4863
4864
    /**
4865
     * Test for the updateContent() method.
4866
     *
4867
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4868
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4869
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4870
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition
4871
     */
4872
    public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields)
4873
    {
4874
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4875
        $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given ContentType');
4876
4877
        $this->assertForUpdateContentContentValidationException(
4878
            $initialLanguageCode,
4879
            $structFields,
4880
            []
4881
        );
4882
    }
4883
4884
    public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation()
4885
    {
4886
        return [
4887
            [
4888
                'eng-US',
4889
                [
4890
                    new Field(
4891
                        [
4892
                            'fieldDefIdentifier' => 'identifier',
4893
                            'value' => 'newValue',
4894
                            'languageCode' => 'eng-US',
4895
                        ]
4896
                    ),
4897
                ],
4898
            ],
4899
        ];
4900
    }
4901
4902
    /**
4903
     * Test for the updateContent() method.
4904
     *
4905
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4906
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4907
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4908
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation
4909
     */
4910
    public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields)
4911
    {
4912
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class);
4913
        $this->expectExceptionMessage('A value is set for non translatable field definition \'identifier\' with language \'eng-US\'');
4914
4915
        $fieldDefinitions = [
4916
            new FieldDefinition(
4917
                [
4918
                    'id' => 'fieldDefinitionId1',
4919
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4920
                    'isTranslatable' => false,
4921
                    'identifier' => 'identifier',
4922
                    'isRequired' => false,
4923
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4924
                ]
4925
            ),
4926
        ];
4927
4928
        $this->assertForUpdateContentContentValidationException(
4929
            $initialLanguageCode,
4930
            $structFields,
4931
            $fieldDefinitions
4932
        );
4933
    }
4934
4935
    public function assertForTestUpdateContentRequiredField(
4936
        $initialLanguageCode,
4937
        $structFields,
4938
        $existingFields,
4939
        $fieldDefinitions
4940
    ) {
4941
        $repositoryMock = $this->getRepositoryMock();
4942
        $permissionResolver = $this->getPermissionResolverMock();
4943
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
4944
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4945
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4946
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4947
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
4948
        $existingLanguageCodes = array_map(
4949
            function (Field $field) {
4950
                return $field->languageCode;
4951
            },
4952
            $existingFields
4953
        );
4954
        $versionInfo = new VersionInfo(
4955
            [
4956
                'contentInfo' => new ContentInfo(
4957
                    [
4958
                        'id' => 42,
4959
                        'contentTypeId' => 24,
4960
                        'mainLanguageCode' => 'eng-GB',
4961
                    ]
4962
                ),
4963
                'versionNo' => 7,
4964
                'languageCodes' => $existingLanguageCodes,
4965
                'status' => VersionInfo::STATUS_DRAFT,
4966
            ]
4967
        );
4968
        $content = new Content(
4969
            [
4970
                'versionInfo' => $versionInfo,
4971
                'internalFields' => $existingFields,
4972
            ]
4973
        );
4974
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4975
4976
        $languageHandlerMock->expects($this->any())
4977
            ->method('loadByLanguageCode')
4978
            ->with($this->isType('string'))
4979
            ->will(
4980
                $this->returnCallback(
4981
                    function () {
4982
                        return new Language(['id' => 4242]);
4983
                    }
4984
                )
4985
            );
4986
4987
        $mockedService->expects($this->once())
4988
            ->method('loadContent')
4989
            ->with(
4990
                $this->equalTo(42),
4991
                $this->equalTo(null),
4992
                $this->equalTo(7)
4993
            )->will(
4994
                $this->returnValue($content)
4995
            );
4996
4997
        $permissionResolver->expects($this->once())
4998
            ->method('canUser')
4999
            ->with(
5000
                $this->equalTo('content'),
5001
                $this->equalTo('edit'),
5002
                $this->equalTo($content),
5003
                $this->isType('array')
5004
            )->will($this->returnValue(true));
5005
5006
        $contentTypeServiceMock->expects($this->once())
5007
            ->method('loadContentType')
5008
            ->with($this->equalTo(24))
5009
            ->will($this->returnValue($contentType));
5010
5011
        $repositoryMock->expects($this->once())
5012
            ->method('getContentTypeService')
5013
            ->will($this->returnValue($contentTypeServiceMock));
5014
5015
        $fieldTypeMock->expects($this->any())
5016
            ->method('acceptValue')
5017
            ->will(
5018
                $this->returnCallback(
5019
                    function ($valueString) {
5020
                        return new ValueStub($valueString);
5021
                    }
5022
                )
5023
            );
5024
5025
        $emptyValue = self::EMPTY_FIELD_VALUE;
5026
        $fieldTypeMock->expects($this->any())
5027
            ->method('isEmptyValue')
5028
            ->will(
5029
                $this->returnCallback(
5030
                    function (ValueStub $value) use ($emptyValue) {
5031
                        return $emptyValue === (string)$value;
5032
                    }
5033
                )
5034
            );
5035
5036
        $fieldTypeMock->expects($this->any())
5037
            ->method('validate')
5038
            ->with(
5039
                $this->isInstanceOf(APIFieldDefinition::class),
5040
                $this->isInstanceOf(Value::class)
5041
            );
5042
5043
        $this->getFieldTypeRegistryMock()->expects($this->any())
5044
            ->method('getFieldType')
5045
            ->will($this->returnValue($fieldTypeMock));
5046
5047
        $contentUpdateStruct = new ContentUpdateStruct(
5048
            [
5049
                'fields' => $structFields,
5050
                'initialLanguageCode' => $initialLanguageCode,
5051
            ]
5052
        );
5053
5054
        return [$content->versionInfo, $contentUpdateStruct];
5055
    }
5056
5057
    public function providerForTestUpdateContentRequiredField()
5058
    {
5059
        return [
5060
            [
5061
                'eng-US',
5062
                [
5063
                    new Field(
5064
                        [
5065
                            'fieldDefIdentifier' => 'identifier',
5066
                            'value' => self::EMPTY_FIELD_VALUE,
5067
                            'languageCode' => null,
5068
                        ]
5069
                    ),
5070
                ],
5071
                'identifier',
5072
                'eng-US',
5073
            ],
5074
        ];
5075
    }
5076
5077
    /**
5078
     * Test for the updateContent() method.
5079
     *
5080
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5081
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5082
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5083
     * @dataProvider providerForTestUpdateContentRequiredField
5084
     */
5085
    public function testUpdateContentRequiredField(
5086
        $initialLanguageCode,
5087
        $structFields,
5088
        $identifier,
5089
        $languageCode
5090
    ) {
5091
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
5092
5093
        $existingFields = [
5094
            new Field(
5095
                [
5096
                    'id' => '100',
5097
                    'fieldDefIdentifier' => 'identifier',
5098
                    'value' => 'initialValue',
5099
                    'languageCode' => 'eng-GB',
5100
                ]
5101
            ),
5102
        ];
5103
        $fieldDefinitions = [
5104
            new FieldDefinition(
5105
                [
5106
                    'id' => 'fieldDefinitionId',
5107
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5108
                    'isTranslatable' => true,
5109
                    'identifier' => 'identifier',
5110
                    'isRequired' => true,
5111
                    'defaultValue' => 'defaultValue',
5112
                ]
5113
            ),
5114
        ];
5115
        list($versionInfo, $contentUpdateStruct) =
5116
            $this->assertForTestUpdateContentRequiredField(
5117
                $initialLanguageCode,
5118
                $structFields,
5119
                $existingFields,
5120
                $fieldDefinitions
5121
            );
5122
5123
        try {
5124
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5125
        } catch (ContentValidationException $e) {
5126
            $this->assertEquals(
5127
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
5128
                $e->getMessage()
5129
            );
5130
5131
            throw $e;
5132
        }
5133
    }
5134
5135
    public function assertForTestUpdateContentThrowsContentFieldValidationException(
5136
        $initialLanguageCode,
5137
        $structFields,
5138
        $existingFields,
5139
        $fieldDefinitions
5140
    ) {
5141
        $repositoryMock = $this->getRepositoryMock();
5142
        $permissionResolverMock = $this->getPermissionResolverMock();
5143
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
5144
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
5145
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
5146
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5147
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
5148
        $existingLanguageCodes = array_map(
5149
            function (Field $field) {
5150
                return $field->languageCode;
5151
            },
5152
            $existingFields
5153
        );
5154
        $languageCodes = $this->determineLanguageCodesForUpdate(
5155
            $initialLanguageCode,
5156
            $structFields,
5157
            $existingLanguageCodes
5158
        );
5159
        $versionInfo = new VersionInfo(
5160
            [
5161
                'contentInfo' => new ContentInfo(
5162
                    [
5163
                        'id' => 42,
5164
                        'contentTypeId' => 24,
5165
                        'mainLanguageCode' => 'eng-GB',
5166
                    ]
5167
                ),
5168
                'versionNo' => 7,
5169
                'languageCodes' => $existingLanguageCodes,
5170
                'status' => VersionInfo::STATUS_DRAFT,
5171
            ]
5172
        );
5173
        $content = new Content(
5174
            [
5175
                'versionInfo' => $versionInfo,
5176
                'internalFields' => $existingFields,
5177
            ]
5178
        );
5179
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
5180
5181
        $languageHandlerMock->expects($this->any())
5182
            ->method('loadByLanguageCode')
5183
            ->with($this->isType('string'))
5184
            ->will(
5185
                $this->returnCallback(
5186
                    function () {
5187
                        return new Language(['id' => 4242]);
5188
                    }
5189
                )
5190
            );
5191
5192
        $mockedService->expects($this->once())
5193
            ->method('loadContent')
5194
            ->with(
5195
                $this->equalTo(42),
5196
                $this->equalTo(null),
5197
                $this->equalTo(7)
5198
            )->will(
5199
                $this->returnValue($content)
5200
            );
5201
5202
        $permissionResolverMock->expects($this->once())
5203
            ->method('canUser')
5204
            ->with(
5205
                $this->equalTo('content'),
5206
                $this->equalTo('edit'),
5207
                $this->equalTo($content),
5208
                $this->isType('array')
5209
            )->will($this->returnValue(true));
5210
5211
        $contentTypeServiceMock->expects($this->once())
5212
            ->method('loadContentType')
5213
            ->with($this->equalTo(24))
5214
            ->will($this->returnValue($contentType));
5215
5216
        $repositoryMock->expects($this->once())
5217
            ->method('getContentTypeService')
5218
            ->will($this->returnValue($contentTypeServiceMock));
5219
5220
        $fieldValues = $this->determineValuesForUpdate(
5221
            $initialLanguageCode,
5222
            $structFields,
5223
            $content,
5224
            $fieldDefinitions,
5225
            $languageCodes
5226
        );
5227
        $allFieldErrors = [];
5228
        $emptyValue = self::EMPTY_FIELD_VALUE;
5229
5230
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5231
            ->method('acceptValue')
5232
            ->will(
5233
                $this->returnCallback(
5234
                    function ($valueString) {
5235
                        return new ValueStub($valueString);
5236
                    }
5237
                )
5238
            );
5239
5240
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5241
            ->method('isEmptyValue')
5242
            ->will(
5243
                $this->returnCallback(
5244
                    function (ValueStub $value) use ($emptyValue) {
5245
                        return $emptyValue === (string)$value;
5246
                    }
5247
                )
5248
            );
5249
5250
        $fieldTypeMock
5251
            ->expects($this->any())
5252
            ->method('validate')
5253
            ->willReturnArgument(1);
5254
5255
        $this->getFieldTypeRegistryMock()->expects($this->any())
5256
            ->method('getFieldType')
5257
            ->will($this->returnValue($fieldTypeMock));
5258
5259
        $contentUpdateStruct = new ContentUpdateStruct(
5260
            [
5261
                'fields' => $structFields,
5262
                'initialLanguageCode' => $initialLanguageCode,
5263
            ]
5264
        );
5265
5266
        return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors];
5267
    }
5268
5269
    public function providerForTestUpdateContentThrowsContentFieldValidationException()
5270
    {
5271
        $allFieldErrors = [
5272
            [
5273
                'fieldDefinitionId1' => [
5274
                    'eng-GB' => 'newValue1-eng-GB',
5275
                    'eng-US' => 'newValue1-eng-GB',
5276
                ],
5277
                'fieldDefinitionId2' => [
5278
                    'eng-GB' => 'initialValue2',
5279
                ],
5280
                'fieldDefinitionId3' => [
5281
                    'eng-GB' => 'initialValue3',
5282
                    'eng-US' => 'initialValue3',
5283
                ],
5284
                'fieldDefinitionId4' => [
5285
                    'eng-GB' => 'initialValue4',
5286
                    'eng-US' => 'newValue4',
5287
                ],
5288
            ],
5289
            [
5290
                'fieldDefinitionId1' => [
5291
                    'eng-GB' => 'newValue1-eng-GB',
5292
                    'eng-US' => 'newValue1-eng-GB',
5293
                ],
5294
                'fieldDefinitionId2' => [
5295
                    'eng-GB' => 'initialValue2',
5296
                ],
5297
                'fieldDefinitionId3' => [
5298
                    'eng-GB' => 'initialValue3',
5299
                    'eng-US' => 'initialValue3',
5300
                ],
5301
                'fieldDefinitionId4' => [
5302
                    'eng-GB' => 'initialValue4',
5303
                    'eng-US' => 'newValue4',
5304
                ],
5305
            ],
5306
            [
5307
                'fieldDefinitionId1' => [
5308
                    'eng-GB' => 'newValue1-eng-GB',
5309
                    'eng-US' => 'newValue1-eng-GB',
5310
                ],
5311
                'fieldDefinitionId2' => [
5312
                    'eng-GB' => 'initialValue2',
5313
                    'eng-US' => 'newValue2',
5314
                ],
5315
                'fieldDefinitionId3' => [
5316
                    'eng-GB' => 'initialValue3',
5317
                    'eng-US' => 'initialValue3',
5318
                ],
5319
                'fieldDefinitionId4' => [
5320
                    'eng-GB' => 'initialValue4',
5321
                    'eng-US' => 'defaultValue4',
5322
                ],
5323
            ],
5324
            [
5325
                'fieldDefinitionId1' => [
5326
                    'eng-GB' => 'newValue1-eng-GB',
5327
                    'eng-US' => 'newValue1-eng-GB',
5328
                ],
5329
                'fieldDefinitionId2' => [
5330
                    'eng-GB' => 'initialValue2',
5331
                    'eng-US' => 'newValue2',
5332
                ],
5333
                'fieldDefinitionId3' => [
5334
                    'eng-GB' => 'initialValue3',
5335
                    'eng-US' => 'initialValue3',
5336
                ],
5337
                'fieldDefinitionId4' => [
5338
                    'eng-GB' => 'initialValue4',
5339
                    'eng-US' => 'defaultValue4',
5340
                ],
5341
            ],
5342
            [
5343
                'fieldDefinitionId1' => [
5344
                    'eng-GB' => 'newValue1-eng-GB',
5345
                    'ger-DE' => 'newValue1-eng-GB',
5346
                    'eng-US' => 'newValue1-eng-GB',
5347
                ],
5348
                'fieldDefinitionId2' => [
5349
                    'eng-GB' => 'initialValue2',
5350
                    'eng-US' => 'newValue2',
5351
                ],
5352
                'fieldDefinitionId3' => [
5353
                    'eng-GB' => 'initialValue3',
5354
                    'ger-DE' => 'initialValue3',
5355
                    'eng-US' => 'initialValue3',
5356
                ],
5357
                'fieldDefinitionId4' => [
5358
                    'eng-GB' => 'initialValue4',
5359
                    'eng-US' => 'defaultValue4',
5360
                    'ger-DE' => 'defaultValue4',
5361
                ],
5362
            ],
5363
            [
5364
                'fieldDefinitionId1' => [
5365
                    'eng-US' => 'newValue1-eng-GB',
5366
                    'ger-DE' => 'newValue1-eng-GB',
5367
                ],
5368
                'fieldDefinitionId2' => [
5369
                    'eng-US' => 'newValue2',
5370
                ],
5371
                'fieldDefinitionId3' => [
5372
                    'ger-DE' => 'initialValue3',
5373
                    'eng-US' => 'initialValue3',
5374
                ],
5375
                'fieldDefinitionId4' => [
5376
                    'ger-DE' => 'defaultValue4',
5377
                    'eng-US' => 'defaultValue4',
5378
                ],
5379
            ],
5380
        ];
5381
5382
        $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex();
5383
        $count = count($data);
5384
        for ($i = 0; $i < $count; ++$i) {
5385
            $data[$i][] = $allFieldErrors[$i];
5386
        }
5387
5388
        return $data;
5389
    }
5390
5391
    /**
5392
     * Test for the updateContent() method.
5393
     *
5394
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5395
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5396
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5397
     * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException
5398
     */
5399
    public function testUpdateContentThrowsContentFieldValidationException($initialLanguageCode, $structFields, $spiField, $allFieldErrors)
5400
    {
5401
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class);
5402
        $this->expectExceptionMessage('Content fields did not validate');
5403
5404
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
5405
        list($versionInfo, $contentUpdateStruct) =
5406
            $this->assertForTestUpdateContentThrowsContentFieldValidationException(
5407
                $initialLanguageCode,
5408
                $structFields,
5409
                $existingFields,
5410
                $fieldDefinitions
5411
            );
5412
5413
        try {
5414
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5415
        } catch (ContentFieldValidationException $e) {
5416
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
5417
            throw $e;
5418
        }
5419
    }
5420
5421
    /**
5422
     * Test for the updateContent() method.
5423
     *
5424
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5425
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5426
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5427
     */
5428
    public function testUpdateContentTransactionRollback()
5429
    {
5430
        $this->expectException(\Exception::class);
5431
        $this->expectExceptionMessage('Store failed');
5432
5433
        $existingFields = [
5434
            new Field(
5435
                [
5436
                    'id' => '100',
5437
                    'fieldDefIdentifier' => 'identifier',
5438
                    'value' => 'initialValue',
5439
                    'languageCode' => 'eng-GB',
5440
                ]
5441
            ),
5442
        ];
5443
5444
        $fieldDefinitions = [
5445
            new FieldDefinition(
5446
                [
5447
                    'id' => 'fieldDefinitionId',
5448
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5449
                    'isTranslatable' => false,
5450
                    'identifier' => 'identifier',
5451
                    'isRequired' => false,
5452
                    'defaultValue' => 'defaultValue',
5453
                ]
5454
            ),
5455
        ];
5456
5457
        // Setup a simple case that will pass
5458
        list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet(
5459
            'eng-US',
5460
            [],
5461
            [],
5462
            $existingFields,
5463
            $fieldDefinitions,
5464
            // Do not execute test
5465
            false
5466
        );
5467
5468
        $repositoryMock = $this->getRepositoryMock();
5469
        $repositoryMock->expects($this->never())->method('commit');
5470
        $repositoryMock->expects($this->once())->method('rollback');
5471
5472
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5473
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5474
        $contentHandlerMock->expects($this->once())
5475
            ->method('updateContent')
5476
            ->with(
5477
                $this->anything(),
5478
                $this->anything(),
5479
                $this->anything()
5480
            )->will($this->throwException(new \Exception('Store failed')));
5481
5482
        // Execute
5483
        $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5484
    }
5485
5486
    /**
5487
     * Test for the copyContent() method.
5488
     *
5489
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5490
     */
5491
    public function testCopyContentThrowsUnauthorizedException()
5492
    {
5493
        $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class);
5494
5495
        $repository = $this->getRepositoryMock();
5496
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
5497
        $contentInfo = $this->createMock(APIContentInfo::class);
5498
        $locationCreateStruct = new LocationCreateStruct();
5499
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5500
        $locationServiceMock = $this->getLocationServiceMock();
5501
        $permissionResolver = $this->getPermissionResolverMock();
5502
5503
        $repository->expects($this->once())
5504
            ->method('getLocationService')
5505
            ->will($this->returnValue($locationServiceMock))
5506
        ;
5507
5508
        $locationServiceMock->expects($this->once())
5509
            ->method('loadLocation')
5510
            ->with(
5511
                $locationCreateStruct->parentLocationId
5512
            )
5513
            ->will($this->returnValue($location))
5514
        ;
5515
5516
        $contentInfo->expects($this->any())
5517
            ->method('__get')
5518
            ->with('sectionId')
5519
            ->will($this->returnValue(42));
5520
5521
        $permissionResolver
5522
            ->method('canUser')
5523
            ->with(
5524
                'content',
5525
                'create',
5526
                $contentInfo,
5527
                [$location]
5528
            )
5529
            ->will($this->returnValue(false));
5530
5531
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
5532
        $contentService->copyContent($contentInfo, $locationCreateStruct);
5533
    }
5534
5535
    /**
5536
     * Test for the copyContent() method.
5537
     *
5538
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5539
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5540
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5541
     */
5542
    public function testCopyContent()
5543
    {
5544
        $repositoryMock = $this->getRepositoryMock();
5545
        $contentService = $this->getPartlyMockedContentService([
5546
            'internalLoadContentInfo',
5547
            'internalLoadContentById',
5548
            'getUnixTimestamp',
5549
        ]);
5550
        $locationServiceMock = $this->getLocationServiceMock();
5551
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5552
        $locationCreateStruct = new LocationCreateStruct();
5553
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5554
        $user = $this->getStubbedUser(14);
5555
5556
        $permissionResolverMock = $this->getPermissionResolverMock();
5557
5558
        $permissionResolverMock
5559
            ->method('getCurrentUserReference')
5560
            ->willReturn($user);
5561
5562
        $repositoryMock
5563
            ->method('getPermissionResolver')
5564
            ->willReturn($permissionResolverMock);
5565
5566
        $repositoryMock->expects($this->exactly(3))
5567
            ->method('getLocationService')
5568
            ->will($this->returnValue($locationServiceMock));
5569
5570
        $locationServiceMock->expects($this->once())
5571
            ->method('loadLocation')
5572
            ->with($locationCreateStruct->parentLocationId)
5573
            ->will($this->returnValue($location))
5574
        ;
5575
5576
        $contentInfoMock->expects($this->any())
5577
            ->method('__get')
5578
            ->will(
5579
                $this->returnValueMap(
5580
                    [
5581
                        ['isHidden', true],
5582
                        ['id', 42],
5583
                    ]
5584
                )
5585
            );
5586
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5587
5588
        $versionInfoMock->expects($this->any())
5589
            ->method('__get')
5590
            ->will(
5591
                $this->returnValueMap(
5592
                    [
5593
                        ['versionNo', 123],
5594
                    ]
5595
                )
5596
            );
5597
5598
        $versionInfoMock->expects($this->once())
5599
            ->method('isDraft')
5600
            ->willReturn(true);
5601
5602
        $versionInfoMock->expects($this->once())
5603
            ->method('getContentInfo')
5604
            ->will($this->returnValue($contentInfoMock));
5605
5606
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5607
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5608
        $domainMapperMock = $this->getDomainMapperMock();
5609
5610
        $repositoryMock->expects($this->once())->method('beginTransaction');
5611
        $repositoryMock->expects($this->once())->method('commit');
5612
5613
        $permissionResolverMock
5614
            ->method('canUser')
5615
            ->willReturnMap(
5616
                [
5617
                    ['content', 'create', $contentInfoMock, [$location], true],
5618
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5619
                ]
5620
            );
5621
5622
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5623
        $spiVersionInfo = new SPIVersionInfo(
5624
            [
5625
                'contentInfo' => $spiContentInfo,
5626
                'creationDate' => 123456,
5627
            ]
5628
        );
5629
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5630
        $contentHandlerMock->expects($this->once())
5631
            ->method('copy')
5632
            ->with(42, null)
5633
            ->will($this->returnValue($spiContent));
5634
5635
        $this->mockGetDefaultObjectStates();
5636
        $this->mockSetDefaultObjectStates();
5637
5638
        $domainMapperMock->expects($this->once())
5639
            ->method('buildVersionInfoDomainObject')
5640
            ->with($spiVersionInfo)
5641
            ->will($this->returnValue($versionInfoMock));
5642
5643
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5644
        $content = $this->mockPublishVersion(123456, 126666, true);
5645
        $locationServiceMock->expects($this->once())
5646
            ->method('createLocation')
5647
            ->with(
5648
                $content->getVersionInfo()->getContentInfo(),
5649
                $locationCreateStruct
5650
            );
5651
5652
        $contentService->expects($this->once())
5653
            ->method('internalLoadContentById')
5654
            ->with(
5655
                $content->id
5656
            )
5657
            ->will($this->returnValue($content));
5658
5659
        $contentService->expects($this->once())
5660
            ->method('getUnixTimestamp')
5661
            ->will($this->returnValue(126666));
5662
5663
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5664
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5665
    }
5666
5667
    /**
5668
     * Test for the copyContent() method.
5669
     *
5670
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5671
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5672
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5673
     */
5674
    public function testCopyContentWithVersionInfo()
5675
    {
5676
        $repositoryMock = $this->getRepositoryMock();
5677
        $contentService = $this->getPartlyMockedContentService([
5678
            'internalLoadContentById',
5679
            'getUnixTimestamp',
5680
        ]);
5681
        $locationServiceMock = $this->getLocationServiceMock();
5682
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5683
        $locationCreateStruct = new LocationCreateStruct();
5684
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5685
        $user = $this->getStubbedUser(14);
5686
5687
        $permissionResolverMock = $this->getPermissionResolverMock();
5688
5689
        $permissionResolverMock
5690
            ->method('getCurrentUserReference')
5691
            ->willReturn($user);
5692
5693
        $repositoryMock
5694
            ->method('getPermissionResolver')
5695
            ->willReturn($permissionResolverMock);
5696
5697
        $repositoryMock->expects($this->exactly(3))
5698
            ->method('getLocationService')
5699
            ->will($this->returnValue($locationServiceMock));
5700
5701
        $locationServiceMock->expects($this->once())
5702
            ->method('loadLocation')
5703
            ->with($locationCreateStruct->parentLocationId)
5704
            ->will($this->returnValue($location))
5705
        ;
5706
5707
        $contentInfoMock->expects($this->any())
5708
            ->method('__get')
5709
            ->will(
5710
                $this->returnValueMap([
5711
                    ['isHidden', true],
5712
                    ['id', 42],
5713
                ])
5714
            );
5715
5716
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5717
5718
        $versionInfoMock->expects($this->any())
5719
            ->method('__get')
5720
            ->will(
5721
                $this->returnValueMap(
5722
                    [
5723
                        ['versionNo', 123],
5724
                    ]
5725
                )
5726
            );
5727
        $versionInfoMock->expects($this->once())
5728
            ->method('isDraft')
5729
            ->willReturn(true);
5730
        $versionInfoMock->expects($this->once())
5731
            ->method('getContentInfo')
5732
            ->will($this->returnValue($contentInfoMock));
5733
5734
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5735
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5736
        $domainMapperMock = $this->getDomainMapperMock();
5737
5738
        $repositoryMock->expects($this->once())->method('beginTransaction');
5739
        $repositoryMock->expects($this->once())->method('commit');
5740
5741
        $permissionResolverMock
5742
            ->method('canUser')
5743
            ->willReturnMap(
5744
                [
5745
                    ['content', 'create', $contentInfoMock, [$location], true],
5746
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5747
                ]
5748
            );
5749
5750
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5751
        $spiVersionInfo = new SPIVersionInfo(
5752
            [
5753
                'contentInfo' => $spiContentInfo,
5754
                'creationDate' => 123456,
5755
            ]
5756
        );
5757
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5758
        $contentHandlerMock->expects($this->once())
5759
            ->method('copy')
5760
            ->with(42, 123)
5761
            ->will($this->returnValue($spiContent));
5762
5763
        $this->mockGetDefaultObjectStates();
5764
        $this->mockSetDefaultObjectStates();
5765
5766
        $domainMapperMock->expects($this->once())
5767
            ->method('buildVersionInfoDomainObject')
5768
            ->with($spiVersionInfo)
5769
            ->will($this->returnValue($versionInfoMock));
5770
5771
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5772
        $content = $this->mockPublishVersion(123456, 126666, true);
5773
        $locationServiceMock->expects($this->once())
5774
            ->method('createLocation')
5775
            ->with(
5776
                $content->getVersionInfo()->getContentInfo(),
5777
                $locationCreateStruct
5778
            );
5779
5780
        $contentService->expects($this->once())
5781
            ->method('internalLoadContentById')
5782
            ->with(
5783
                $content->id
5784
            )
5785
            ->will($this->returnValue($content));
5786
5787
        $contentService->expects($this->once())
5788
            ->method('getUnixTimestamp')
5789
            ->will($this->returnValue(126666));
5790
5791
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5792
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock);
5793
    }
5794
5795
    /**
5796
     * Test for the copyContent() method.
5797
     *
5798
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5799
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5800
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5801
     */
5802
    public function testCopyContentWithRollback()
5803
    {
5804
        $this->expectException(\Exception::class);
5805
        $this->expectExceptionMessage('Handler threw an exception');
5806
5807
        $repositoryMock = $this->getRepositoryMock();
5808
        $contentService = $this->getPartlyMockedContentService();
5809
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5810
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5811
        $locationCreateStruct = new LocationCreateStruct();
5812
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5813
        $locationServiceMock = $this->getLocationServiceMock();
5814
        $user = $this->getStubbedUser(14);
5815
5816
        $permissionResolverMock = $this->getPermissionResolverMock();
5817
5818
        $permissionResolverMock
5819
            ->method('getCurrentUserReference')
5820
            ->willReturn($user);
5821
5822
        $repositoryMock
5823
            ->method('getPermissionResolver')
5824
            ->willReturn($permissionResolverMock);
5825
5826
        $repositoryMock->expects($this->once())
5827
            ->method('getLocationService')
5828
            ->will($this->returnValue($locationServiceMock))
5829
        ;
5830
5831
        $locationServiceMock->expects($this->once())
5832
            ->method('loadLocation')
5833
            ->with($locationCreateStruct->parentLocationId)
5834
            ->will($this->returnValue($location))
5835
        ;
5836
5837
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5838
        $contentInfoMock->expects($this->any())
5839
            ->method('__get')
5840
            ->with('id')
5841
            ->will($this->returnValue(42));
5842
5843
        $this->mockGetDefaultObjectStates();
5844
5845
        $repositoryMock->expects($this->once())->method('beginTransaction');
5846
        $repositoryMock->expects($this->once())->method('rollback');
5847
5848
        $permissionResolverMock
5849
            ->method('canUser')
5850
            ->willReturnMap(
5851
                [
5852
                    ['content', 'create', $contentInfoMock, [$location], true],
5853
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5854
                ]
5855
            );
5856
5857
        $contentHandlerMock->expects($this->once())
5858
            ->method('copy')
5859
            ->with(42, null)
5860
            ->will($this->throwException(new Exception('Handler threw an exception')));
5861
5862
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5863
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5864
    }
5865
5866
    /**
5867
     * Reusable method for setting exceptions on buildContentDomainObject usage.
5868
     *
5869
     * Plain usage as in when content type is loaded directly.
5870
     *
5871
     * @param \eZ\Publish\SPI\Persistence\Content $spiContent
5872
     * @param array $translations
5873
     * @param bool $useAlwaysAvailable
5874
     *
5875
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Values\Content\Content
5876
     */
5877
    private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null)
5878
    {
5879
        $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId;
5880
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5881
        $repositoryMock = $this->getRepositoryMock();
5882
5883
        $contentType = new ContentType([
5884
            'id' => $contentTypeId,
5885
            'fieldDefinitions' => [],
5886
        ]);
5887
5888
        $repositoryMock->expects($this->once())
5889
            ->method('getContentTypeService')
5890
            ->willReturn($contentTypeServiceMock);
5891
5892
        $contentTypeServiceMock->expects($this->once())
5893
            ->method('loadContentType')
5894
            ->with($this->equalTo($contentTypeId))
5895
            ->willReturn($contentType);
5896
5897
        $content = $this->createMock(APIContent::class);
5898
5899
        $this->getDomainMapperMock()
5900
            ->expects($this->once())
5901
            ->method('buildContentDomainObject')
5902
            ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable)
5903
            ->willReturn($content);
5904
5905
        return $content;
5906
    }
5907
5908
    protected function mockGetDefaultObjectStates()
5909
    {
5910
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5911
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5912
5913
        $objectStateGroups = [
5914
            new SPIObjectStateGroup(['id' => 10]),
5915
            new SPIObjectStateGroup(['id' => 20]),
5916
        ];
5917
5918
        /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5919
        $objectStateHandlerMock->expects($this->once())
5920
            ->method('loadAllGroups')
5921
            ->will($this->returnValue($objectStateGroups));
5922
5923
        $objectStateHandlerMock->expects($this->at(1))
5924
            ->method('loadObjectStates')
5925
            ->with($this->equalTo(10))
5926
            ->will(
5927
                $this->returnValue(
5928
                    [
5929
                        new SPIObjectState(['id' => 11, 'groupId' => 10]),
5930
                        new SPIObjectState(['id' => 12, 'groupId' => 10]),
5931
                    ]
5932
                )
5933
            );
5934
5935
        $objectStateHandlerMock->expects($this->at(2))
5936
            ->method('loadObjectStates')
5937
            ->with($this->equalTo(20))
5938
            ->will(
5939
                $this->returnValue(
5940
                    [
5941
                        new SPIObjectState(['id' => 21, 'groupId' => 20]),
5942
                        new SPIObjectState(['id' => 22, 'groupId' => 20]),
5943
                    ]
5944
                )
5945
            );
5946
    }
5947
5948
    protected function mockSetDefaultObjectStates()
5949
    {
5950
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5951
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5952
5953
        $defaultObjectStates = [
5954
            new SPIObjectState(['id' => 11, 'groupId' => 10]),
5955
            new SPIObjectState(['id' => 21, 'groupId' => 20]),
5956
        ];
5957
        foreach ($defaultObjectStates as $index => $objectState) {
5958
            $objectStateHandlerMock->expects($this->at($index + 3))
5959
                ->method('setContentState')
5960
                ->with(
5961
                    42,
5962
                    $objectState->groupId,
5963
                    $objectState->id
5964
                );
5965
        }
5966
    }
5967
5968
    /**
5969
     * @param int|null $publicationDate
5970
     * @param int|null $modificationDate
5971
     * @param bool $isHidden
5972
     *
5973
     * @return \eZ\Publish\API\Repository\Values\Content\Content
5974
     */
5975
    protected function mockPublishVersion($publicationDate = null, $modificationDate = null, $isHidden = false)
5976
    {
5977
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5978
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5979
        /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5980
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5981
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
5982
5983
        $spiContent = new SPIContent([
5984
            'versionInfo' => new VersionInfo([
5985
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
5986
            ]),
5987
        ]);
5988
5989
        $contentMock = $this->mockBuildContentDomainObject($spiContent);
5990
        $contentMock->expects($this->any())
5991
            ->method('__get')
5992
            ->will(
5993
                $this->returnValueMap(
5994
                    [
5995
                        ['id', 42],
5996
                        ['contentInfo', $contentInfoMock],
5997
                        ['versionInfo', $versionInfoMock],
5998
                    ]
5999
                )
6000
            );
6001
        $contentMock->expects($this->any())
6002
            ->method('getVersionInfo')
6003
            ->will($this->returnValue($versionInfoMock));
6004
        $versionInfoMock->expects($this->any())
6005
            ->method('getContentInfo')
6006
            ->will($this->returnValue($contentInfoMock));
6007
        $versionInfoMock->expects($this->any())
6008
            ->method('__get')
6009
            ->will(
6010
                $this->returnValueMap(
6011
                    [
6012
                        ['languageCodes', ['eng-GB']],
6013
                    ]
6014
                )
6015
            );
6016
        $contentInfoMock->expects($this->any())
6017
            ->method('__get')
6018
            ->will(
6019
                $this->returnValueMap(
6020
                    [
6021
                        ['alwaysAvailable', true],
6022
                        ['mainLanguageCode', 'eng-GB'],
6023
                    ]
6024
                )
6025
            );
6026
6027
        $currentTime = time();
6028
        if ($publicationDate === null && $versionInfoMock->versionNo === 1) {
6029
            $publicationDate = $currentTime;
6030
        }
6031
6032
        // Account for 1 second of test execution time
6033
        $metadataUpdateStruct->publicationDate = $publicationDate;
6034
        $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime;
6035
        $metadataUpdateStruct->isHidden = $isHidden;
6036
6037
        $contentHandlerMock->expects($this->once())
6038
            ->method('publish')
6039
            ->with(
6040
                42,
6041
                123,
6042
                $metadataUpdateStruct
6043
            )
6044
            ->will($this->returnValue($spiContent));
6045
6046
        /* @var \eZ\Publish\API\Repository\Values\Content\Content $contentMock */
6047
        $this->mockPublishUrlAliasesForContent($contentMock);
6048
6049
        return $contentMock;
6050
    }
6051
6052
    /**
6053
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
6054
     */
6055
    protected function mockPublishUrlAliasesForContent(APIContent $content)
6056
    {
6057
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
6058
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */
6059
        $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler();
6060
        $locationServiceMock = $this->getLocationServiceMock();
6061
        $location = $this->createMock(APILocation::class);
6062
6063
        $location->expects($this->at(0))
6064
            ->method('__get')
6065
            ->with('id')
6066
            ->will($this->returnValue(123));
6067
        $location->expects($this->at(1))
6068
            ->method('__get')
6069
            ->with('parentLocationId')
6070
            ->will($this->returnValue(456));
6071
6072
        $urlAliasNames = ['eng-GB' => 'hello'];
6073
        $nameSchemaServiceMock->expects($this->once())
6074
            ->method('resolveUrlAliasSchema')
6075
            ->with($content)
6076
            ->will($this->returnValue($urlAliasNames));
6077
6078
        $locationServiceMock->expects($this->once())
6079
            ->method('loadLocations')
6080
            ->with($content->getVersionInfo()->getContentInfo())
6081
            ->will($this->returnValue([$location]));
6082
6083
        $urlAliasHandlerMock->expects($this->once())
6084
            ->method('publishUrlAliasForLocation')
6085
            ->with(123, 456, 'hello', 'eng-GB', true, true);
6086
6087
        $location->expects($this->at(2))
6088
            ->method('__get')
6089
            ->with('id')
6090
            ->will($this->returnValue(123));
6091
6092
        $location->expects($this->at(3))
6093
            ->method('__get')
6094
            ->with('parentLocationId')
6095
            ->will($this->returnValue(456));
6096
6097
        $urlAliasHandlerMock->expects($this->once())
6098
            ->method('archiveUrlAliasesForDeletedTranslations')
6099
            ->with(123, 456, ['eng-GB']);
6100
    }
6101
6102
    protected $domainMapperMock;
6103
6104
    /**
6105
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\DomainMapper
6106
     */
6107
    protected function getDomainMapperMock()
6108
    {
6109
        if (!isset($this->domainMapperMock)) {
6110
            $this->domainMapperMock = $this->createMock(DomainMapper::class);
6111
        }
6112
6113
        return $this->domainMapperMock;
6114
    }
6115
6116
    protected $relationProcessorMock;
6117
6118
    /**
6119
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\RelationProcessor
6120
     */
6121
    protected function getRelationProcessorMock()
6122
    {
6123
        if (!isset($this->relationProcessorMock)) {
6124
            $this->relationProcessorMock = $this->createMock(RelationProcessor::class);
6125
        }
6126
6127
        return $this->relationProcessorMock;
6128
    }
6129
6130
    protected $nameSchemaServiceMock;
6131
6132
    /**
6133
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService
6134
     */
6135
    protected function getNameSchemaServiceMock()
6136
    {
6137
        if (!isset($this->nameSchemaServiceMock)) {
6138
            $this->nameSchemaServiceMock = $this->createMock(NameSchemaService::class);
6139
        }
6140
6141
        return $this->nameSchemaServiceMock;
6142
    }
6143
6144
    protected $contentTypeServiceMock;
6145
6146
    /**
6147
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentTypeService
6148
     */
6149
    protected function getContentTypeServiceMock()
6150
    {
6151
        if (!isset($this->contentTypeServiceMock)) {
6152
            $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class);
6153
        }
6154
6155
        return $this->contentTypeServiceMock;
6156
    }
6157
6158
    protected $locationServiceMock;
6159
6160
    /**
6161
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\LocationService
6162
     */
6163
    protected function getLocationServiceMock()
6164
    {
6165
        if (!isset($this->locationServiceMock)) {
6166
            $this->locationServiceMock = $this->createMock(APILocationService::class);
6167
        }
6168
6169
        return $this->locationServiceMock;
6170
    }
6171
6172
    /** @var \eZ\Publish\Core\Repository\ContentService */
6173
    protected $partlyMockedContentService;
6174
6175
    /**
6176
     * Returns the content service to test with $methods mocked.
6177
     *
6178
     * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()}
6179
     *
6180
     * @param string[] $methods
6181
     *
6182
     * @return \eZ\Publish\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject
6183
     */
6184
    protected function getPartlyMockedContentService(array $methods = null)
6185
    {
6186
        if (!isset($this->partlyMockedContentService)) {
6187
            $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class)
6188
                ->setMethods($methods)
6189
                ->setConstructorArgs(
6190
                    [
6191
                        $this->getRepositoryMock(),
6192
                        $this->getPersistenceMock(),
6193
                        $this->getDomainMapperMock(),
6194
                        $this->getRelationProcessorMock(),
6195
                        $this->getNameSchemaServiceMock(),
6196
                        $this->getFieldTypeRegistryMock(),
6197
                        $this->getPermissionResolverMock(),
6198
                        [],
6199
                    ]
6200
                )
6201
                ->getMock();
6202
        }
6203
6204
        return $this->partlyMockedContentService;
6205
    }
6206
6207
    /**
6208
     * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject
6209
     */
6210
    protected function getRepositoryMock(): Repository
6211
    {
6212
        $repositoryMock = parent::getRepositoryMock();
6213
        $repositoryMock
6214
            ->expects($this->any())
6215
            ->method('getPermissionResolver')
6216
            ->willReturn($this->getPermissionResolverMock());
6217
6218
        return $repositoryMock;
6219
    }
6220
}
6221