Completed
Push — readme ( 4da83f...7adc69 )
by
unknown
28:00 queued 04:49
created

providerForTestUpdateContentThrowsBadStateException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
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()
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
        $settings = ['default_version_archive_limit' => 10];
81
82
        $service = new ContentService(
83
            $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...
84
            $persistenceHandlerMock,
85
            $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...
86
            $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...
87
            $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...
88
            $fieldTypeRegistryMock,
0 ignored issues
show
Bug introduced by
It seems like $fieldTypeRegistryMock defined by $this->getFieldTypeRegistryMock() on line 79 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\FieldTypeRegistry>, 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
            $settings
90
        );
91
92
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
93
            $repositoryMock,
94
            'repository',
95
            $service
96
        );
97
98
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
99
            $persistenceHandlerMock,
100
            'persistenceHandler',
101
            $service
102
        );
103
104
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
105
            $domainMapperMock,
106
            'domainMapper',
107
            $service
108
        );
109
110
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
111
            $relationProcessorMock,
112
            'relationProcessor',
113
            $service
114
        );
115
116
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
117
            $nameSchemaServiceMock,
118
            'nameSchemaService',
119
            $service
120
        );
121
122
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

Loading history...
123
            $fieldTypeRegistryMock,
124
            'fieldTypeRegistry',
125
            $service
126
        );
127
128
        $this->assertAttributeSame(
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit\Framework\Assert::assertAttributeSame() has been deprecated with message: https://github.com/sebastianbergmann/phpunit/issues/3338

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

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

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

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

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

Loading history...
1131
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
1132
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
1133
                    } else {
1134
                        $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1135
                    }
1136
                    continue;
1137
                }
1138
1139 View Code Duplication
                if (isset($mappedStructFields[$identifier][$languageCode])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1140
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
1141
                    continue;
1142
                }
1143
1144
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
1145
            }
1146
        }
1147
1148
        return $this->stubValues($values);
1149
    }
1150
1151
    /**
1152
     * @param string $mainLanguageCode
1153
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1154
     *
1155
     * @return string[]
1156
     */
1157
    protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields)
1158
    {
1159
        $languageCodes = [];
1160
1161
        foreach ($structFields as $field) {
1162
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
1163
                continue;
1164
            }
1165
1166
            $languageCodes[$field->languageCode] = true;
1167
        }
1168
1169
        $languageCodes[$mainLanguageCode] = true;
1170
1171
        return array_keys($languageCodes);
1172
    }
1173
1174
    /**
1175
     * Asserts that calling createContent() with given API field set causes calling
1176
     * Handler::createContent() with given SPI field set.
1177
     *
1178
     * @param string $mainLanguageCode
1179
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
1180
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
1181
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
1182
     * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs
1183
     * @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...
1184
     * @param bool $execute
1185
     *
1186
     * @return mixed
1187
     */
1188
    protected function assertForTestCreateContentNonRedundantFieldSet(
1189
        $mainLanguageCode,
1190
        array $structFields,
1191
        array $spiFields,
1192
        array $fieldDefinitions,
1193
        array $locationCreateStructs = [],
1194
        $withObjectStates = false,
1195
        $execute = true
1196
    ) {
1197
        $repositoryMock = $this->getRepositoryMock();
1198
        $mockedService = $this->getPartlyMockedContentService();
1199
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
1200
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
1201
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1202
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1203
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
1204
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
1205
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1206
        $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...
1207
        $domainMapperMock = $this->getDomainMapperMock();
1208
        $relationProcessorMock = $this->getRelationProcessorMock();
1209
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
1210
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
1211
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
1212
        $contentType = new ContentType(
1213
            [
1214
                'id' => 123,
1215
                'fieldDefinitions' => $fieldDefinitions,
1216
                'nameSchema' => '<nameSchema>',
1217
            ]
1218
        );
1219
        $contentCreateStruct = new ContentCreateStruct(
1220
            [
1221
                'fields' => $structFields,
1222
                'mainLanguageCode' => $mainLanguageCode,
1223
                'contentType' => $contentType,
1224
                'alwaysAvailable' => false,
1225
                'ownerId' => 169,
1226
                'sectionId' => 1,
1227
            ]
1228
        );
1229
1230
        $languageHandlerMock->expects($this->any())
1231
            ->method('loadByLanguageCode')
1232
            ->with($this->isType('string'))
1233
            ->will(
1234
                $this->returnCallback(
1235
                    function () {
1236
                        return new Language(['id' => 4242]);
1237
                    }
1238
                )
1239
            );
1240
1241
        $repositoryMock->expects($this->once())->method('beginTransaction');
1242
1243
        $contentTypeServiceMock->expects($this->once())
1244
            ->method('loadContentType')
1245
            ->with($this->equalTo($contentType->id))
1246
            ->will($this->returnValue($contentType));
1247
1248
        $repositoryMock->expects($this->once())
1249
            ->method('getContentTypeService')
1250
            ->will($this->returnValue($contentTypeServiceMock));
1251
1252
        $that = $this;
1253
        $repositoryMock->expects($this->once())
1254
            ->method('canUser')
1255
            ->with(
1256
                $this->equalTo('content'),
1257
                $this->equalTo('create'),
1258
                $this->isInstanceOf(APIContentCreateStruct::class),
1259
                $this->equalTo($locationCreateStructs)
1260
            )->will(
1261
                $this->returnCallback(
1262
                    function () use ($that, $contentCreateStruct) {
1263
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1264
1265
                        return true;
1266
                    }
1267
                )
1268
            );
1269
1270
        $domainMapperMock->expects($this->once())
1271
            ->method('getUniqueHash')
1272
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1273
            ->will(
1274
                $this->returnCallback(
1275
                    function ($object) use ($that, $contentCreateStruct) {
1276
                        $that->assertEquals($contentCreateStruct, $object);
1277
1278
                        return 'hash';
1279
                    }
1280
                )
1281
            );
1282
1283
        $fieldTypeMock->expects($this->any())
1284
            ->method('acceptValue')
1285
            ->will(
1286
                $this->returnCallback(
1287
                    function ($valueString) {
1288
                        return new ValueStub($valueString);
1289
                    }
1290
                )
1291
            );
1292
1293
        $fieldTypeMock->expects($this->any())
1294
            ->method('toPersistenceValue')
1295
            ->will(
1296
                $this->returnCallback(
1297
                    function (ValueStub $value) {
1298
                        return (string)$value;
1299
                    }
1300
                )
1301
            );
1302
1303
        $emptyValue = self::EMPTY_FIELD_VALUE;
1304
        $fieldTypeMock->expects($this->any())
1305
            ->method('isEmptyValue')
1306
            ->will(
1307
                $this->returnCallback(
1308
                    function (ValueStub $value) use ($emptyValue) {
1309
                        return $emptyValue === (string)$value;
1310
                    }
1311
                )
1312
            );
1313
1314
        $fieldTypeMock->expects($this->any())
1315
            ->method('validate')
1316
            ->will($this->returnValue([]));
1317
1318
        $this->getFieldTypeRegistryMock()->expects($this->any())
1319
            ->method('getFieldType')
1320
            ->will($this->returnValue($fieldTypeMock));
1321
1322
        $relationProcessorMock
1323
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
1324
            ->method('appendFieldRelations')
1325
            ->with(
1326
                $this->isType('array'),
1327
                $this->isType('array'),
1328
                $this->isInstanceOf(SPIFieldType::class),
1329
                $this->isInstanceOf(Value::class),
1330
                $this->anything()
1331
            );
1332
1333
        $values = $this->determineValuesForCreate(
1334
            $mainLanguageCode,
1335
            $structFields,
1336
            $fieldDefinitions,
1337
            $languageCodes
1338
        );
1339
        $nameSchemaServiceMock->expects($this->once())
1340
            ->method('resolve')
1341
            ->with(
1342
                $this->equalTo($contentType->nameSchema),
1343
                $this->equalTo($contentType),
1344
                $this->equalTo($values),
1345
                $this->equalTo($languageCodes)
1346
            )->will($this->returnValue([]));
1347
1348
        $relationProcessorMock->expects($this->any())
1349
            ->method('processFieldRelations')
1350
            ->with(
1351
                $this->isType('array'),
1352
                $this->equalTo(42),
1353
                $this->isType('int'),
1354
                $this->equalTo($contentType),
1355
                $this->equalTo([])
1356
            );
1357
1358
        if (!$withObjectStates) {
1359
            $objectStateHandlerMock->expects($this->once())
1360
                ->method('loadAllGroups')
1361
                ->will($this->returnValue([]));
1362
        }
1363
1364
        if ($execute) {
1365
            $spiContentCreateStruct = new SPIContentCreateStruct(
1366
                [
1367
                    'name' => [],
1368
                    'typeId' => 123,
1369
                    'sectionId' => 1,
1370
                    'ownerId' => 169,
1371
                    'remoteId' => 'hash',
1372
                    'fields' => $spiFields,
1373
                    'modified' => time(),
1374
                    'initialLanguageId' => 4242,
1375
                ]
1376
            );
1377
            $spiContentCreateStruct2 = clone $spiContentCreateStruct;
1378
            ++$spiContentCreateStruct2->modified;
1379
1380
            $spiContent = new SPIContent(
1381
                [
1382
                    'versionInfo' => new SPIContent\VersionInfo(
1383
                        [
1384
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
1385
                            'versionNo' => 7,
1386
                        ]
1387
                    ),
1388
                ]
1389
            );
1390
1391
            $contentHandlerMock->expects($this->once())
1392
                ->method('create')
1393
                ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
1394
                ->will($this->returnValue($spiContent));
1395
1396
            $repositoryMock->expects($this->once())->method('commit');
1397
            $domainMapperMock->expects($this->once())
1398
                ->method('buildContentDomainObject')
1399
                ->with(
1400
                    $this->isInstanceOf(SPIContent::class),
1401
                    $this->equalTo($contentType)
1402
                );
1403
1404
            $mockedService->createContent($contentCreateStruct, []);
1405
        }
1406
1407
        return $contentCreateStruct;
1408
    }
1409
1410
    public function providerForTestCreateContentNonRedundantFieldSet1()
1411
    {
1412
        $spiFields = [
1413
            new SPIField(
1414
                [
1415
                    'fieldDefinitionId' => 'fieldDefinitionId',
1416
                    'type' => 'fieldTypeIdentifier',
1417
                    'value' => 'newValue',
1418
                    'languageCode' => 'eng-US',
1419
                ]
1420
            ),
1421
        ];
1422
1423
        return [
1424
            // 0. Without language set
1425
            [
1426
                'eng-US',
1427
                [
1428
                    new Field(
1429
                        [
1430
                            'fieldDefIdentifier' => 'identifier',
1431
                            'value' => 'newValue',
1432
                            'languageCode' => 'eng-US',
1433
                        ]
1434
                    ),
1435
                ],
1436
                $spiFields,
1437
            ],
1438
            // 1. Without language set
1439
            [
1440
                'eng-US',
1441
                [
1442
                    new Field(
1443
                        [
1444
                            'fieldDefIdentifier' => 'identifier',
1445
                            'value' => 'newValue',
1446
                            'languageCode' => null,
1447
                        ]
1448
                    ),
1449
                ],
1450
                $spiFields,
1451
            ],
1452
        ];
1453
    }
1454
1455
    /**
1456
     * Test for the createContent() method.
1457
     *
1458
     * Testing the simplest use case.
1459
     *
1460
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1461
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1462
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1463
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1464
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1465
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet1
1466
     */
1467 View Code Duplication
    public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields)
1468
    {
1469
        $fieldDefinitions = [
1470
            new FieldDefinition(
1471
                [
1472
                    'id' => 'fieldDefinitionId',
1473
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1474
                    'isTranslatable' => false,
1475
                    'identifier' => 'identifier',
1476
                    'isRequired' => false,
1477
                    'defaultValue' => 'defaultValue',
1478
                ]
1479
            ),
1480
        ];
1481
1482
        $this->assertForTestCreateContentNonRedundantFieldSet(
1483
            $mainLanguageCode,
1484
            $structFields,
1485
            $spiFields,
1486
            $fieldDefinitions
1487
        );
1488
    }
1489
1490
    public function providerForTestCreateContentNonRedundantFieldSet2()
1491
    {
1492
        $spiFields = [
1493
            new SPIField(
1494
                [
1495
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1496
                    'type' => 'fieldTypeIdentifier',
1497
                    'value' => 'newValue1',
1498
                    'languageCode' => 'eng-US',
1499
                ]
1500
            ),
1501
            new SPIField(
1502
                [
1503
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1504
                    'type' => 'fieldTypeIdentifier',
1505
                    'value' => 'newValue2',
1506
                    'languageCode' => 'ger-DE',
1507
                ]
1508
            ),
1509
        ];
1510
1511
        return [
1512
            // 0. With language set
1513
            [
1514
                'eng-US',
1515
                [
1516
                    new Field(
1517
                        [
1518
                            'fieldDefIdentifier' => 'identifier1',
1519
                            'value' => 'newValue1',
1520
                            'languageCode' => 'eng-US',
1521
                        ]
1522
                    ),
1523
                    new Field(
1524
                        [
1525
                            'fieldDefIdentifier' => 'identifier2',
1526
                            'value' => 'newValue2',
1527
                            'languageCode' => 'ger-DE',
1528
                        ]
1529
                    ),
1530
                ],
1531
                $spiFields,
1532
            ],
1533
            // 1. Without language set
1534
            [
1535
                'eng-US',
1536
                [
1537
                    new Field(
1538
                        [
1539
                            'fieldDefIdentifier' => 'identifier1',
1540
                            'value' => 'newValue1',
1541
                            'languageCode' => null,
1542
                        ]
1543
                    ),
1544
                    new Field(
1545
                        [
1546
                            'fieldDefIdentifier' => 'identifier2',
1547
                            'value' => 'newValue2',
1548
                            'languageCode' => 'ger-DE',
1549
                        ]
1550
                    ),
1551
                ],
1552
                $spiFields,
1553
            ],
1554
        ];
1555
    }
1556
1557
    /**
1558
     * Test for the createContent() method.
1559
     *
1560
     * Testing multiple languages with multiple translatable fields with empty default value.
1561
     *
1562
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1563
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1564
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1565
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1566
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1567
     * @dataProvider providerForTestCreateContentNonRedundantFieldSet2
1568
     */
1569
    public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields)
1570
    {
1571
        $fieldDefinitions = [
1572
            new FieldDefinition(
1573
                [
1574
                    'id' => 'fieldDefinitionId1',
1575
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1576
                    'isTranslatable' => true,
1577
                    'identifier' => 'identifier1',
1578
                    'isRequired' => false,
1579
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1580
                ]
1581
            ),
1582
            new FieldDefinition(
1583
                [
1584
                    'id' => 'fieldDefinitionId2',
1585
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1586
                    'isTranslatable' => true,
1587
                    'identifier' => 'identifier2',
1588
                    'isRequired' => false,
1589
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1590
                ]
1591
            ),
1592
        ];
1593
1594
        $this->assertForTestCreateContentNonRedundantFieldSet(
1595
            $mainLanguageCode,
1596
            $structFields,
1597
            $spiFields,
1598
            $fieldDefinitions
1599
        );
1600
    }
1601
1602
    public function providerForTestCreateContentNonRedundantFieldSetComplex()
1603
    {
1604
        $spiFields0 = [
1605
            new SPIField(
1606
                [
1607
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1608
                    'type' => 'fieldTypeIdentifier',
1609
                    'value' => 'defaultValue2',
1610
                    'languageCode' => 'eng-US',
1611
                ]
1612
            ),
1613
            new SPIField(
1614
                [
1615
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1616
                    'type' => 'fieldTypeIdentifier',
1617
                    'value' => 'defaultValue4',
1618
                    'languageCode' => 'eng-US',
1619
                ]
1620
            ),
1621
        ];
1622
        $spiFields1 = [
1623
            new SPIField(
1624
                [
1625
                    'fieldDefinitionId' => 'fieldDefinitionId1',
1626
                    'type' => 'fieldTypeIdentifier',
1627
                    'value' => 'newValue1',
1628
                    'languageCode' => 'ger-DE',
1629
                ]
1630
            ),
1631
            new SPIField(
1632
                [
1633
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1634
                    'type' => 'fieldTypeIdentifier',
1635
                    'value' => 'defaultValue2',
1636
                    'languageCode' => 'ger-DE',
1637
                ]
1638
            ),
1639
            new SPIField(
1640
                [
1641
                    'fieldDefinitionId' => 'fieldDefinitionId2',
1642
                    'type' => 'fieldTypeIdentifier',
1643
                    'value' => 'newValue2',
1644
                    'languageCode' => 'eng-US',
1645
                ]
1646
            ),
1647
            new SPIField(
1648
                [
1649
                    'fieldDefinitionId' => 'fieldDefinitionId4',
1650
                    'type' => 'fieldTypeIdentifier',
1651
                    'value' => 'newValue4',
1652
                    'languageCode' => 'eng-US',
1653
                ]
1654
            ),
1655
        ];
1656
1657
        return [
1658
            // 0. Creating by default values only
1659
            [
1660
                'eng-US',
1661
                [],
1662
                $spiFields0,
1663
            ],
1664
            // 1. Multiple languages with language set
1665
            [
1666
                'eng-US',
1667
                [
1668
                    new Field(
1669
                        [
1670
                            'fieldDefIdentifier' => 'identifier1',
1671
                            'value' => 'newValue1',
1672
                            'languageCode' => 'ger-DE',
1673
                        ]
1674
                    ),
1675
                    new Field(
1676
                        [
1677
                            'fieldDefIdentifier' => 'identifier2',
1678
                            'value' => 'newValue2',
1679
                            'languageCode' => 'eng-US',
1680
                        ]
1681
                    ),
1682
                    new Field(
1683
                        [
1684
                            'fieldDefIdentifier' => 'identifier4',
1685
                            'value' => 'newValue4',
1686
                            'languageCode' => 'eng-US',
1687
                        ]
1688
                    ),
1689
                ],
1690
                $spiFields1,
1691
            ],
1692
            // 2. Multiple languages without language set
1693
            [
1694
                'eng-US',
1695
                [
1696
                    new Field(
1697
                        [
1698
                            'fieldDefIdentifier' => 'identifier1',
1699
                            'value' => 'newValue1',
1700
                            'languageCode' => 'ger-DE',
1701
                        ]
1702
                    ),
1703
                    new Field(
1704
                        [
1705
                            'fieldDefIdentifier' => 'identifier2',
1706
                            'value' => 'newValue2',
1707
                            'languageCode' => null,
1708
                        ]
1709
                    ),
1710
                    new Field(
1711
                        [
1712
                            'fieldDefIdentifier' => 'identifier4',
1713
                            'value' => 'newValue4',
1714
                            'languageCode' => null,
1715
                        ]
1716
                    ),
1717
                ],
1718
                $spiFields1,
1719
            ],
1720
        ];
1721
    }
1722
1723
    protected function fixturesForTestCreateContentNonRedundantFieldSetComplex()
1724
    {
1725
        return [
1726
            new FieldDefinition(
1727
                [
1728
                    'id' => 'fieldDefinitionId1',
1729
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1730
                    'isTranslatable' => true,
1731
                    'identifier' => 'identifier1',
1732
                    'isRequired' => false,
1733
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1734
                ]
1735
            ),
1736
            new FieldDefinition(
1737
                [
1738
                    'id' => 'fieldDefinitionId2',
1739
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1740
                    'isTranslatable' => true,
1741
                    'identifier' => 'identifier2',
1742
                    'isRequired' => false,
1743
                    'defaultValue' => 'defaultValue2',
1744
                ]
1745
            ),
1746
            new FieldDefinition(
1747
                [
1748
                    'id' => 'fieldDefinitionId3',
1749
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1750
                    'isTranslatable' => false,
1751
                    'identifier' => 'identifier3',
1752
                    'isRequired' => false,
1753
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
1754
                ]
1755
            ),
1756
            new FieldDefinition(
1757
                [
1758
                    'id' => 'fieldDefinitionId4',
1759
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
1760
                    'isTranslatable' => false,
1761
                    'identifier' => 'identifier4',
1762
                    'isRequired' => false,
1763
                    'defaultValue' => 'defaultValue4',
1764
                ]
1765
            ),
1766
        ];
1767
    }
1768
1769
    /**
1770
     * Test for the createContent() method.
1771
     *
1772
     * Testing multiple languages with multiple translatable fields with empty default value.
1773
     *
1774
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1775
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1776
     * @covers \eZ\Publish\Core\Repository\ContentService::cloneField
1777
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
1778
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1779
     * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex
1780
     */
1781
    public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields)
1782
    {
1783
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
1784
1785
        $this->assertForTestCreateContentNonRedundantFieldSet(
1786
            $mainLanguageCode,
1787
            $structFields,
1788
            $spiFields,
1789
            $fieldDefinitions
1790
        );
1791
    }
1792
1793 View Code Duplication
    public function providerForTestCreateContentWithInvalidLanguage()
1794
    {
1795
        return [
1796
            [
1797
                'eng-GB',
1798
                [
1799
                    new Field(
1800
                        [
1801
                            'fieldDefIdentifier' => 'identifier',
1802
                            'value' => 'newValue',
1803
                            'languageCode' => 'Klingon',
1804
                        ]
1805
                    ),
1806
                ],
1807
            ],
1808
            [
1809
                'Klingon',
1810
                [
1811
                    new Field(
1812
                        [
1813
                            'fieldDefIdentifier' => 'identifier',
1814
                            'value' => 'newValue',
1815
                            'languageCode' => 'eng-GB',
1816
                        ]
1817
                    ),
1818
                ],
1819
            ],
1820
        ];
1821
    }
1822
1823
    /**
1824
     * Test for the updateContent() method.
1825
     *
1826
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1827
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1828
     * @dataProvider providerForTestCreateContentWithInvalidLanguage
1829
     * @expectedException \eZ\Publish\API\Repository\Exceptions\NotFoundException
1830
     * @expectedExceptionMessage Could not find 'Language' with identifier 'Klingon'
1831
     */
1832
    public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields)
1833
    {
1834
        $repositoryMock = $this->getRepositoryMock();
1835
        $mockedService = $this->getPartlyMockedContentService();
1836
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
1837
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
1838
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1839
        $domainMapperMock = $this->getDomainMapperMock();
1840
        $contentType = new ContentType(
1841
            [
1842
                'id' => 123,
1843
                'fieldDefinitions' => [],
1844
            ]
1845
        );
1846
        $contentCreateStruct = new ContentCreateStruct(
1847
            [
1848
                'fields' => $structFields,
1849
                'mainLanguageCode' => $mainLanguageCode,
1850
                'contentType' => $contentType,
1851
                'alwaysAvailable' => false,
1852
                'ownerId' => 169,
1853
                'sectionId' => 1,
1854
            ]
1855
        );
1856
1857
        $languageHandlerMock->expects($this->any())
1858
            ->method('loadByLanguageCode')
1859
            ->with($this->isType('string'))
1860
            ->will(
1861
                $this->returnCallback(
1862 View Code Duplication
                    function ($languageCode) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1863
                        if ($languageCode === 'Klingon') {
1864
                            throw new NotFoundException('Language', 'Klingon');
1865
                        }
1866
1867
                        return new Language(['id' => 4242]);
1868
                    }
1869
                )
1870
            );
1871
1872
        $contentTypeServiceMock->expects($this->once())
1873
            ->method('loadContentType')
1874
            ->with($this->equalTo($contentType->id))
1875
            ->will($this->returnValue($contentType));
1876
1877
        $repositoryMock->expects($this->once())
1878
            ->method('getContentTypeService')
1879
            ->will($this->returnValue($contentTypeServiceMock));
1880
1881
        $that = $this;
1882
        $repositoryMock->expects($this->once())
1883
            ->method('canUser')
1884
            ->with(
1885
                $this->equalTo('content'),
1886
                $this->equalTo('create'),
1887
                $this->isInstanceOf(APIContentCreateStruct::class),
1888
                $this->equalTo([])
1889
            )->will(
1890
                $this->returnCallback(
1891
                    function () use ($that, $contentCreateStruct) {
1892
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
1893
1894
                        return true;
1895
                    }
1896
                )
1897
            );
1898
1899
        $domainMapperMock->expects($this->once())
1900
            ->method('getUniqueHash')
1901
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
1902
            ->will(
1903
                $this->returnCallback(
1904
                    function ($object) use ($that, $contentCreateStruct) {
1905
                        $that->assertEquals($contentCreateStruct, $object);
1906
1907
                        return 'hash';
1908
                    }
1909
                )
1910
            );
1911
1912
        $mockedService->createContent($contentCreateStruct, []);
1913
    }
1914
1915
    protected function assertForCreateContentContentValidationException(
1916
        $mainLanguageCode,
1917
        $structFields,
1918
        $fieldDefinitions = []
1919
    ) {
1920
        $repositoryMock = $this->getRepositoryMock();
1921
        $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']);
1922
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
1923
        $contentType = new ContentType(
1924
            [
1925
                'id' => 123,
1926
                'fieldDefinitions' => $fieldDefinitions,
1927
            ]
1928
        );
1929
        $contentCreateStruct = new ContentCreateStruct(
1930
            [
1931
                'ownerId' => 169,
1932
                'alwaysAvailable' => false,
1933
                'remoteId' => 'faraday',
1934
                'mainLanguageCode' => $mainLanguageCode,
1935
                'fields' => $structFields,
1936
                'contentType' => $contentType,
1937
            ]
1938
        );
1939
1940
        $contentTypeServiceMock->expects($this->once())
1941
            ->method('loadContentType')
1942
            ->with($this->equalTo(123))
1943
            ->will($this->returnValue($contentType));
1944
1945
        $repositoryMock->expects($this->once())
1946
            ->method('getContentTypeService')
1947
            ->will($this->returnValue($contentTypeServiceMock));
1948
1949
        $repositoryMock->expects($this->once())
1950
            ->method('canUser')
1951
            ->with(
1952
                $this->equalTo('content'),
1953
                $this->equalTo('create'),
1954
                $this->isInstanceOf(get_class($contentCreateStruct)),
1955
                $this->equalTo([])
1956
            )->will($this->returnValue(true));
1957
1958
        $mockedService->expects($this->once())
1959
            ->method('loadContentByRemoteId')
1960
            ->with($contentCreateStruct->remoteId)
1961
            ->will(
1962
                $this->throwException(new NotFoundException('Content', 'faraday'))
1963
            );
1964
1965
        $mockedService->createContent($contentCreateStruct, []);
1966
    }
1967
1968 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition()
1969
    {
1970
        return [
1971
            [
1972
                'eng-GB',
1973
                [
1974
                    new Field(
1975
                        [
1976
                            'fieldDefIdentifier' => 'identifier',
1977
                            'value' => 'newValue',
1978
                            'languageCode' => 'eng-GB',
1979
                        ]
1980
                    ),
1981
                ],
1982
            ],
1983
        ];
1984
    }
1985
1986
    /**
1987
     * Test for the createContent() method.
1988
     *
1989
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
1990
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
1991
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
1992
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition
1993
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentValidationException
1994
     * @expectedExceptionMessage Field definition 'identifier' does not exist in given ContentType
1995
     */
1996
    public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields)
1997
    {
1998
        $this->assertForCreateContentContentValidationException(
1999
            $mainLanguageCode,
2000
            $structFields,
2001
            []
2002
        );
2003
    }
2004
2005 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionTranslation()
2006
    {
2007
        return [
2008
            [
2009
                'eng-GB',
2010
                [
2011
                    new Field(
2012
                        [
2013
                            'fieldDefIdentifier' => 'identifier',
2014
                            'value' => 'newValue',
2015
                            'languageCode' => 'eng-US',
2016
                        ]
2017
                    ),
2018
                ],
2019
            ],
2020
        ];
2021
    }
2022
2023
    /**
2024
     * Test for the createContent() method.
2025
     *
2026
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2027
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2028
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2029
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2030
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentValidationException
2031
     * @expectedExceptionMessage A value is set for non translatable field definition 'identifier' with language 'eng-US'
2032
     */
2033 View Code Duplication
    public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields)
2034
    {
2035
        $fieldDefinitions = [
2036
            new FieldDefinition(
2037
                [
2038
                    'id' => 'fieldDefinitionId1',
2039
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2040
                    'isTranslatable' => false,
2041
                    'identifier' => 'identifier',
2042
                    'isRequired' => false,
2043
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
2044
                ]
2045
            ),
2046
        ];
2047
2048
        $this->assertForCreateContentContentValidationException(
2049
            $mainLanguageCode,
2050
            $structFields,
2051
            $fieldDefinitions
2052
        );
2053
    }
2054
2055
    /**
2056
     * Asserts behaviour necessary for testing ContentFieldValidationException because of required
2057
     * field being empty.
2058
     *
2059
     * @param string $mainLanguageCode
2060
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2061
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2062
     *
2063
     * @return mixed
2064
     */
2065
    protected function assertForTestCreateContentRequiredField(
2066
        $mainLanguageCode,
2067
        array $structFields,
2068
        array $fieldDefinitions
2069
    ) {
2070
        $repositoryMock = $this->getRepositoryMock();
2071
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2072
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2073
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2074
        $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...
2075
        $domainMapperMock = $this->getDomainMapperMock();
2076
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2077
        $contentType = new ContentType(
2078
            [
2079
                'id' => 123,
2080
                'fieldDefinitions' => $fieldDefinitions,
2081
                'nameSchema' => '<nameSchema>',
2082
            ]
2083
        );
2084
        $contentCreateStruct = new ContentCreateStruct(
2085
            [
2086
                'fields' => $structFields,
2087
                'mainLanguageCode' => $mainLanguageCode,
2088
                'contentType' => $contentType,
2089
                'alwaysAvailable' => false,
2090
                'ownerId' => 169,
2091
                'sectionId' => 1,
2092
            ]
2093
        );
2094
2095
        $languageHandlerMock->expects($this->any())
2096
            ->method('loadByLanguageCode')
2097
            ->with($this->isType('string'))
2098
            ->will(
2099
                $this->returnCallback(
2100
                    function () {
2101
                        return new Language(['id' => 4242]);
2102
                    }
2103
                )
2104
            );
2105
2106
        $contentTypeServiceMock->expects($this->once())
2107
            ->method('loadContentType')
2108
            ->with($this->equalTo($contentType->id))
2109
            ->will($this->returnValue($contentType));
2110
2111
        $repositoryMock->expects($this->once())
2112
            ->method('getContentTypeService')
2113
            ->will($this->returnValue($contentTypeServiceMock));
2114
2115
        $that = $this;
2116
        $repositoryMock->expects($this->once())
2117
            ->method('canUser')
2118
            ->with(
2119
                $this->equalTo('content'),
2120
                $this->equalTo('create'),
2121
                $this->isInstanceOf(APIContentCreateStruct::class),
2122
                $this->equalTo([])
2123
            )->will(
2124
                $this->returnCallback(
2125
                    function () use ($that, $contentCreateStruct) {
2126
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2127
2128
                        return true;
2129
                    }
2130
                )
2131
            );
2132
2133
        $domainMapperMock->expects($this->once())
2134
            ->method('getUniqueHash')
2135
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2136
            ->will(
2137
                $this->returnCallback(
2138
                    function ($object) use ($that, $contentCreateStruct) {
2139
                        $that->assertEquals($contentCreateStruct, $object);
2140
2141
                        return 'hash';
2142
                    }
2143
                )
2144
            );
2145
2146
        $fieldTypeMock->expects($this->any())
2147
            ->method('acceptValue')
2148
            ->will(
2149
                $this->returnCallback(
2150
                    function ($valueString) {
2151
                        return new ValueStub($valueString);
2152
                    }
2153
                )
2154
            );
2155
2156
        $emptyValue = self::EMPTY_FIELD_VALUE;
2157
        $fieldTypeMock->expects($this->any())
2158
            ->method('isEmptyValue')
2159
            ->will(
2160
                $this->returnCallback(
2161
                    function (ValueStub $value) use ($emptyValue) {
2162
                        return $emptyValue === (string)$value;
2163
                    }
2164
                )
2165
            );
2166
2167
        $fieldTypeMock->expects($this->any())
2168
            ->method('validate')
2169
            ->will($this->returnValue([]));
2170
2171
        $this->getFieldTypeRegistryMock()->expects($this->any())
2172
            ->method('getFieldType')
2173
            ->will($this->returnValue($fieldTypeMock));
2174
2175
        return $contentCreateStruct;
2176
    }
2177
2178 View Code Duplication
    public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField()
2179
    {
2180
        return [
2181
            [
2182
                'eng-US',
2183
                [
2184
                    new Field(
2185
                        [
2186
                            'fieldDefIdentifier' => 'identifier',
2187
                            'value' => self::EMPTY_FIELD_VALUE,
2188
                            'languageCode' => null,
2189
                        ]
2190
                    ),
2191
                ],
2192
                'identifier',
2193
                'eng-US',
2194
            ],
2195
        ];
2196
    }
2197
2198
    /**
2199
     * Test for the createContent() method.
2200
     *
2201
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2202
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2203
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2204
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField
2205
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
2206
     */
2207
    public function testCreateContentRequiredField(
2208
        $mainLanguageCode,
2209
        $structFields,
2210
        $identifier,
2211
        $languageCode
2212
    ) {
2213
        $fieldDefinitions = [
2214
            new FieldDefinition(
2215
                [
2216
                    'id' => 'fieldDefinitionId',
2217
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2218
                    'isTranslatable' => true,
2219
                    'identifier' => 'identifier',
2220
                    'isRequired' => true,
2221
                    'defaultValue' => 'defaultValue',
2222
                ]
2223
            ),
2224
        ];
2225
        $contentCreateStruct = $this->assertForTestCreateContentRequiredField(
2226
            $mainLanguageCode,
2227
            $structFields,
2228
            $fieldDefinitions
2229
        );
2230
2231
        $mockedService = $this->getPartlyMockedContentService();
2232
2233
        try {
2234
            $mockedService->createContent($contentCreateStruct, []);
2235
        } catch (ContentValidationException $e) {
2236
            $this->assertEquals(
2237
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
2238
                $e->getMessage()
2239
            );
2240
2241
            throw $e;
2242
        }
2243
    }
2244
2245
    /**
2246
     * Asserts behaviour necessary for testing ContentFieldValidationException because of
2247
     * field not being valid.
2248
     *
2249
     * @param string $mainLanguageCode
2250
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2251
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2252
     *
2253
     * @return mixed
2254
     */
2255
    protected function assertForTestCreateContentThrowsContentFieldValidationException(
2256
        $mainLanguageCode,
2257
        array $structFields,
2258
        array $fieldDefinitions
2259
    ) {
2260
        $repositoryMock = $this->getRepositoryMock();
2261
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2262
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2263
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2264
        $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...
2265
        $domainMapperMock = $this->getDomainMapperMock();
2266
        $relationProcessorMock = $this->getRelationProcessorMock();
2267
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
2268
        $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields);
2269
        $contentType = new ContentType(
2270
            [
2271
                'id' => 123,
2272
                'fieldDefinitions' => $fieldDefinitions,
2273
                'nameSchema' => '<nameSchema>',
2274
            ]
2275
        );
2276
        $contentCreateStruct = new ContentCreateStruct(
2277
            [
2278
                'fields' => $structFields,
2279
                'mainLanguageCode' => $mainLanguageCode,
2280
                'contentType' => $contentType,
2281
                'alwaysAvailable' => false,
2282
                'ownerId' => 169,
2283
                'sectionId' => 1,
2284
            ]
2285
        );
2286
2287
        $languageHandlerMock->expects($this->any())
2288
            ->method('loadByLanguageCode')
2289
            ->with($this->isType('string'))
2290
            ->will(
2291
                $this->returnCallback(
2292
                    function () {
2293
                        return new Language(['id' => 4242]);
2294
                    }
2295
                )
2296
            );
2297
2298
        $contentTypeServiceMock->expects($this->once())
2299
            ->method('loadContentType')
2300
            ->with($this->equalTo($contentType->id))
2301
            ->will($this->returnValue($contentType));
2302
2303
        $repositoryMock->expects($this->once())
2304
            ->method('getContentTypeService')
2305
            ->will($this->returnValue($contentTypeServiceMock));
2306
2307
        $that = $this;
2308
        $repositoryMock->expects($this->once())
2309
            ->method('canUser')
2310
            ->with(
2311
                $this->equalTo('content'),
2312
                $this->equalTo('create'),
2313
                $this->isInstanceOf(APIContentCreateStruct::class),
2314
                $this->equalTo([])
2315
            )->will(
2316
                $this->returnCallback(
2317
                    function () use ($that, $contentCreateStruct) {
2318
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2319
2320
                        return true;
2321
                    }
2322
                )
2323
            );
2324
2325
        $domainMapperMock->expects($this->once())
2326
            ->method('getUniqueHash')
2327
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2328
            ->will(
2329
                $this->returnCallback(
2330
                    function ($object) use ($that, $contentCreateStruct) {
2331
                        $that->assertEquals($contentCreateStruct, $object);
2332
2333
                        return 'hash';
2334
                    }
2335
                )
2336
            );
2337
2338
        $this->getFieldTypeRegistryMock()->expects($this->any())
2339
            ->method('getFieldType')
2340
            ->will($this->returnValue($fieldTypeMock));
2341
2342
        $relationProcessorMock
2343
            ->expects($this->any())
2344
            ->method('appendFieldRelations')
2345
            ->with(
2346
                $this->isType('array'),
2347
                $this->isType('array'),
2348
                $this->isInstanceOf(SPIFieldType::class),
2349
                $this->isInstanceOf(Value::class),
2350
                $this->anything()
2351
            );
2352
2353
        $fieldValues = $this->determineValuesForCreate(
2354
            $mainLanguageCode,
2355
            $structFields,
2356
            $fieldDefinitions,
2357
            $languageCodes
2358
        );
2359
        $allFieldErrors = [];
2360
        $validateCount = 0;
2361
        $emptyValue = self::EMPTY_FIELD_VALUE;
2362
        foreach ($contentType->getFieldDefinitions() as $fieldDefinition) {
2363
            foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) {
2364
                $fieldTypeMock->expects($this->at($validateCount++))
2365
                    ->method('acceptValue')
2366
                    ->will(
2367
                        $this->returnCallback(
2368
                            function ($valueString) {
2369
                                return new ValueStub($valueString);
2370
                            }
2371
                        )
2372
                    );
2373
2374
                $fieldTypeMock->expects($this->at($validateCount++))
2375
                    ->method('isEmptyValue')
2376
                    ->will(
2377
                        $this->returnCallback(
2378
                            function (ValueStub $value) use ($emptyValue) {
2379
                                return $emptyValue === (string)$value;
2380
                            }
2381
                        )
2382
                    );
2383
2384
                if (self::EMPTY_FIELD_VALUE === (string)$value) {
2385
                    continue;
2386
                }
2387
2388
                $fieldTypeMock->expects($this->at($validateCount++))
2389
                    ->method('validate')
2390
                    ->with(
2391
                        $this->equalTo($fieldDefinition),
2392
                        $this->equalTo($value)
2393
                    )->will($this->returnArgument(1));
2394
2395
                $allFieldErrors[$fieldDefinition->id][$languageCode] = $value;
2396
            }
2397
        }
2398
2399
        return [$contentCreateStruct, $allFieldErrors];
2400
    }
2401
2402
    public function providerForTestCreateContentThrowsContentFieldValidationException()
2403
    {
2404
        return $this->providerForTestCreateContentNonRedundantFieldSetComplex();
2405
    }
2406
2407
    /**
2408
     * Test for the createContent() method.
2409
     *
2410
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2411
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2412
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2413
     * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException
2414
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
2415
     * @expectedExceptionMessage Content fields did not validate
2416
     */
2417
    public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields)
2418
    {
2419
        $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex();
2420
        list($contentCreateStruct, $allFieldErrors) =
2421
            $this->assertForTestCreateContentThrowsContentFieldValidationException(
2422
                $mainLanguageCode,
2423
                $structFields,
2424
                $fieldDefinitions
2425
            );
2426
2427
        $mockedService = $this->getPartlyMockedContentService();
2428
2429
        try {
2430
            $mockedService->createContent($contentCreateStruct);
2431
        } catch (ContentFieldValidationException $e) {
2432
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
2433
            throw $e;
2434
        }
2435
    }
2436
2437
    /**
2438
     * Test for the createContent() method.
2439
     *
2440
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2441
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2442
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2443
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2444
     */
2445
    public function testCreateContentWithLocations()
2446
    {
2447
        $spiFields = [
2448
            new SPIField(
2449
                [
2450
                    'fieldDefinitionId' => 'fieldDefinitionId',
2451
                    'type' => 'fieldTypeIdentifier',
2452
                    'value' => 'defaultValue',
2453
                    'languageCode' => 'eng-US',
2454
                ]
2455
            ),
2456
        ];
2457
        $fieldDefinitions = [
2458
            new FieldDefinition(
2459
                [
2460
                    'id' => 'fieldDefinitionId',
2461
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2462
                    'isTranslatable' => false,
2463
                    'identifier' => 'identifier',
2464
                    'isRequired' => false,
2465
                    'defaultValue' => 'defaultValue',
2466
                ]
2467
            ),
2468
        ];
2469
2470
        // Set up a simple case that will pass
2471
        $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]);
2472
        $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]);
2473
        $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2];
2474
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2475
            'eng-US',
2476
            [],
2477
            $spiFields,
2478
            $fieldDefinitions,
2479
            $locationCreateStructs,
2480
            false,
2481
            // Do not execute
2482
            false
2483
        );
2484
2485
        $repositoryMock = $this->getRepositoryMock();
2486
        $mockedService = $this->getPartlyMockedContentService();
2487
        $locationServiceMock = $this->getLocationServiceMock();
2488
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2489
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2490
        $domainMapperMock = $this->getDomainMapperMock();
2491
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2492
        $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]);
2493
2494
        $locationServiceMock->expects($this->at(0))
2495
            ->method('loadLocation')
2496
            ->with($this->equalTo(321))
2497
            ->will($this->returnValue($parentLocation));
2498
2499
        $locationServiceMock->expects($this->at(1))
2500
            ->method('loadLocation')
2501
            ->with($this->equalTo(654))
2502
            ->will($this->returnValue($parentLocation));
2503
2504
        $repositoryMock->expects($this->atLeastOnce())
2505
            ->method('getLocationService')
2506
            ->will($this->returnValue($locationServiceMock));
2507
2508
        $domainMapperMock->expects($this->at(1))
2509
            ->method('buildSPILocationCreateStruct')
2510
            ->with(
2511
                $this->equalTo($locationCreateStruct1),
2512
                $this->equalTo($parentLocation),
2513
                $this->equalTo(true),
2514
                $this->equalTo(null),
2515
                $this->equalTo(null)
2516
            )->will($this->returnValue($spiLocationCreateStruct));
2517
2518
        $domainMapperMock->expects($this->at(2))
2519
            ->method('buildSPILocationCreateStruct')
2520
            ->with(
2521
                $this->equalTo($locationCreateStruct2),
2522
                $this->equalTo($parentLocation),
2523
                $this->equalTo(false),
2524
                $this->equalTo(null),
2525
                $this->equalTo(null)
2526
            )->will($this->returnValue($spiLocationCreateStruct));
2527
2528
        $spiContentCreateStruct = new SPIContentCreateStruct(
2529
            [
2530
                'name' => [],
2531
                'typeId' => 123,
2532
                'sectionId' => 1,
2533
                'ownerId' => 169,
2534
                'remoteId' => 'hash',
2535
                'fields' => $spiFields,
2536
                'modified' => time(),
2537
                'initialLanguageId' => 4242,
2538
                'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct],
2539
            ]
2540
        );
2541
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2542
        ++$spiContentCreateStruct2->modified;
2543
2544
        $spiContent = new SPIContent(
2545
            [
2546
                'versionInfo' => new SPIContent\VersionInfo(
2547
                    [
2548
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2549
                        'versionNo' => 7,
2550
                    ]
2551
                ),
2552
            ]
2553
        );
2554
2555
        $handlerMock->expects($this->once())
2556
            ->method('create')
2557
            ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2))
2558
            ->will($this->returnValue($spiContent));
2559
2560
        $domainMapperMock->expects($this->once())
2561
            ->method('buildContentDomainObject')
2562
            ->with(
2563
                $this->isInstanceOf(SPIContent::class),
2564
                $this->isInstanceOf(APIContentType::class)
2565
            );
2566
2567
        $repositoryMock->expects($this->once())->method('commit');
2568
2569
        // Execute
2570
        $mockedService->createContent($contentCreateStruct, $locationCreateStructs);
2571
    }
2572
2573
    /**
2574
     * Test for the createContent() method.
2575
     *
2576
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2577
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2578
     * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs
2579
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2580
     * @expectedException \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
2581
     * @expectedExceptionMessage Multiple LocationCreateStructs with the same parent Location '321' are given
2582
     */
2583
    public function testCreateContentWithLocationsDuplicateUnderParent()
2584
    {
2585
        $fieldDefinitions = [
2586
            new FieldDefinition(
2587
                [
2588
                    'id' => 'fieldDefinitionId',
2589
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2590
                    'isTranslatable' => false,
2591
                    'identifier' => 'identifier',
2592
                    'isRequired' => false,
2593
                    'defaultValue' => 'defaultValue',
2594
                ]
2595
            ),
2596
        ];
2597
2598
        $repositoryMock = $this->getRepositoryMock();
2599
        $mockedService = $this->getPartlyMockedContentService();
2600
        $locationServiceMock = $this->getLocationServiceMock();
2601
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
2602
        $domainMapperMock = $this->getDomainMapperMock();
2603
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
2604
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
2605
        $spiLocationCreateStruct = new SPILocation\CreateStruct();
2606
        $parentLocation = new Location(['id' => 321]);
2607
        $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]);
2608
        $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct];
2609
        $contentType = new ContentType(
2610
            [
2611
                'id' => 123,
2612
                'fieldDefinitions' => $fieldDefinitions,
2613
                'nameSchema' => '<nameSchema>',
2614
            ]
2615
        );
2616
        $contentCreateStruct = new ContentCreateStruct(
2617
            [
2618
                'fields' => [],
2619
                'mainLanguageCode' => 'eng-US',
2620
                'contentType' => $contentType,
2621
                'alwaysAvailable' => false,
2622
                'ownerId' => 169,
2623
                'sectionId' => 1,
2624
            ]
2625
        );
2626
2627
        $languageHandlerMock->expects($this->any())
2628
            ->method('loadByLanguageCode')
2629
            ->with($this->isType('string'))
2630
            ->will(
2631
                $this->returnCallback(
2632
                    function () {
2633
                        return new Language(['id' => 4242]);
2634
                    }
2635
                )
2636
            );
2637
2638
        $contentTypeServiceMock->expects($this->once())
2639
            ->method('loadContentType')
2640
            ->with($this->equalTo($contentType->id))
2641
            ->will($this->returnValue($contentType));
2642
2643
        $repositoryMock->expects($this->once())
2644
            ->method('getContentTypeService')
2645
            ->will($this->returnValue($contentTypeServiceMock));
2646
2647
        $that = $this;
2648
        $repositoryMock->expects($this->once())
2649
            ->method('canUser')
2650
            ->with(
2651
                $this->equalTo('content'),
2652
                $this->equalTo('create'),
2653
                $this->isInstanceOf(APIContentCreateStruct::class),
2654
                $this->equalTo($locationCreateStructs)
2655
            )->will(
2656
                $this->returnCallback(
2657
                    function () use ($that, $contentCreateStruct) {
2658
                        $that->assertEquals($contentCreateStruct, func_get_arg(2));
2659
2660
                        return true;
2661
                    }
2662
                )
2663
            );
2664
2665
        $domainMapperMock->expects($this->once())
2666
            ->method('getUniqueHash')
2667
            ->with($this->isInstanceOf(APIContentCreateStruct::class))
2668
            ->will(
2669
                $this->returnCallback(
2670
                    function ($object) use ($that, $contentCreateStruct) {
2671
                        $that->assertEquals($contentCreateStruct, $object);
2672
2673
                        return 'hash';
2674
                    }
2675
                )
2676
            );
2677
2678
        $locationServiceMock->expects($this->once())
2679
            ->method('loadLocation')
2680
            ->with($this->equalTo(321))
2681
            ->will($this->returnValue($parentLocation));
2682
2683
        $repositoryMock->expects($this->any())
2684
            ->method('getLocationService')
2685
            ->will($this->returnValue($locationServiceMock));
2686
2687
        $domainMapperMock->expects($this->any())
2688
            ->method('buildSPILocationCreateStruct')
2689
            ->with(
2690
                $this->equalTo($locationCreateStruct),
2691
                $this->equalTo($parentLocation),
2692
                $this->equalTo(true),
2693
                $this->equalTo(null),
2694
                $this->equalTo(null)
2695
            )->will($this->returnValue($spiLocationCreateStruct));
2696
2697
        $mockedService->createContent(
2698
            $contentCreateStruct,
2699
            $locationCreateStructs
2700
        );
2701
    }
2702
2703
    /**
2704
     * Test for the createContent() method.
2705
     *
2706
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2707
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2708
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2709
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2710
     */
2711
    public function testCreateContentObjectStates()
2712
    {
2713
        $spiFields = [
2714
            new SPIField(
2715
                [
2716
                    'fieldDefinitionId' => 'fieldDefinitionId',
2717
                    'type' => 'fieldTypeIdentifier',
2718
                    'value' => 'defaultValue',
2719
                    'languageCode' => 'eng-US',
2720
                ]
2721
            ),
2722
        ];
2723
        $fieldDefinitions = [
2724
            new FieldDefinition(
2725
                [
2726
                    'id' => 'fieldDefinitionId',
2727
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2728
                    'isTranslatable' => false,
2729
                    'identifier' => 'identifier',
2730
                    'isRequired' => false,
2731
                    'defaultValue' => 'defaultValue',
2732
                ]
2733
            ),
2734
        ];
2735
        $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...
2736
            new SPIObjectStateGroup(['id' => 10]),
2737
            new SPIObjectStateGroup(['id' => 20]),
2738
        ];
2739
2740
        // Set up a simple case that will pass
2741
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2742
            'eng-US',
2743
            [],
2744
            $spiFields,
2745
            $fieldDefinitions,
2746
            [],
2747
            true,
2748
            // Do not execute
2749
            false
2750
        );
2751
        $timestamp = time();
2752
        $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}");
2753
2754
        $repositoryMock = $this->getRepositoryMock();
2755
        $mockedService = $this->getPartlyMockedContentService();
2756
        /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */
2757
        $handlerMock = $this->getPersistenceMock()->contentHandler();
2758
        $domainMapperMock = $this->getDomainMapperMock();
2759
2760
        $this->mockGetDefaultObjectStates();
2761
        $this->mockSetDefaultObjectStates();
2762
2763
        $spiContentCreateStruct = new SPIContentCreateStruct(
2764
            [
2765
                'name' => [],
2766
                'typeId' => 123,
2767
                'sectionId' => 1,
2768
                'ownerId' => 169,
2769
                'remoteId' => 'hash',
2770
                'fields' => $spiFields,
2771
                'modified' => $timestamp,
2772
                'initialLanguageId' => 4242,
2773
                'locations' => [],
2774
            ]
2775
        );
2776
        $spiContentCreateStruct2 = clone $spiContentCreateStruct;
2777
        ++$spiContentCreateStruct2->modified;
2778
2779
        $spiContent = new SPIContent(
2780
            [
2781
                'versionInfo' => new SPIContent\VersionInfo(
2782
                    [
2783
                        'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
2784
                        'versionNo' => 7,
2785
                    ]
2786
                ),
2787
            ]
2788
        );
2789
2790
        $handlerMock->expects($this->once())
2791
            ->method('create')
2792
            ->with($this->equalTo($spiContentCreateStruct))
2793
            ->will($this->returnValue($spiContent));
2794
2795
        $domainMapperMock->expects($this->once())
2796
            ->method('buildContentDomainObject')
2797
            ->with(
2798
                $this->isInstanceOf(SPIContent::class),
2799
                $this->isInstanceOf(APIContentType::class)
2800
            );
2801
2802
        $repositoryMock->expects($this->once())->method('commit');
2803
2804
        // Execute
2805
        $mockedService->createContent($contentCreateStruct, []);
2806
    }
2807
2808
    /**
2809
     * Test for the createContent() method.
2810
     *
2811
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate
2812
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate
2813
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
2814
     * @covers \eZ\Publish\Core\Repository\ContentService::createContent
2815
     * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation
2816
     * @expectedException \Exception
2817
     * @expectedExceptionMessage Store failed
2818
     */
2819
    public function testCreateContentWithRollback()
2820
    {
2821
        $fieldDefinitions = [
2822
            new FieldDefinition(
2823
                [
2824
                    'id' => 'fieldDefinitionId',
2825
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
2826
                    'isTranslatable' => false,
2827
                    'identifier' => 'identifier',
2828
                    'isRequired' => false,
2829
                    'defaultValue' => 'defaultValue',
2830
                ]
2831
            ),
2832
        ];
2833
2834
        // Setup a simple case that will pass
2835
        $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet(
2836
            'eng-US',
2837
            [],
2838
            [],
2839
            $fieldDefinitions,
2840
            [],
2841
            false,
2842
            // Do not execute test
2843
            false
2844
        );
2845
2846
        $repositoryMock = $this->getRepositoryMock();
2847
        $repositoryMock->expects($this->never())->method('commit');
2848
        $repositoryMock->expects($this->once())->method('rollback');
2849
2850
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
2851
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
2852
        $contentHandlerMock->expects($this->once())
2853
            ->method('create')
2854
            ->with($this->anything())
2855
            ->will($this->throwException(new \Exception('Store failed')));
2856
2857
        // Execute
2858
        $this->partlyMockedContentService->createContent($contentCreateStruct, []);
2859
    }
2860
2861
    public function providerForTestUpdateContentThrowsBadStateException()
2862
    {
2863
        return [
2864
            [VersionInfo::STATUS_PUBLISHED],
2865
            [VersionInfo::STATUS_ARCHIVED],
2866
        ];
2867
    }
2868
2869
    /**
2870
     * Test for the updateContent() method.
2871
     *
2872
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2873
     * @expectedException \eZ\Publish\API\Repository\Exceptions\BadStateException
2874
     * @dataProvider providerForTestUpdateContentThrowsBadStateException
2875
     */
2876
    public function testUpdateContentThrowsBadStateException($status)
2877
    {
2878
        $versionInfo = new VersionInfo(
2879
            [
2880
                'contentInfo' => new ContentInfo(['id' => 42]),
2881
                'versionNo' => 7,
2882
                'status' => $status,
2883
            ]
2884
        );
2885
        $content = new Content(
2886
            [
2887
                'versionInfo' => $versionInfo,
2888
                'internalFields' => [],
2889
            ]
2890
        );
2891
2892
        $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContent']);
2893
        $mockedService
2894
            ->method('loadContent')
2895
            ->with(
2896
                $this->equalTo(42),
2897
                $this->equalTo(null),
2898
                $this->equalTo(7)
2899
            )->will(
2900
                $this->returnValue($content)
2901
            );
2902
        $mockedService
2903
            ->method('internalLoadContent')
2904
            ->will(
2905
                $this->returnValue($content)
2906
            );
2907
2908
        $permissionResolverMock = $this->getPermissionResolverMock();
2909
        $permissionResolverMock
2910
            ->method('canUser')
2911
            ->will($this->returnValue(true));
2912
2913
        $contentUpdateStruct = new ContentUpdateStruct();
2914
2915
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2916
    }
2917
2918
    /**
2919
     * Test for the updateContent() method.
2920
     *
2921
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
2922
     * @expectedException \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
2923
     */
2924
    public function testUpdateContentThrowsUnauthorizedException()
2925
    {
2926
        $permissionResolverMock = $this->getPermissionResolverMock();
2927
        $mockedService = $this->getPartlyMockedContentService(['loadContent']);
2928
        $contentUpdateStruct = new ContentUpdateStruct();
2929
        $versionInfo = new VersionInfo(
2930
            [
2931
                'contentInfo' => new ContentInfo(['id' => 42]),
2932
                'versionNo' => 7,
2933
                'status' => VersionInfo::STATUS_DRAFT,
2934
            ]
2935
        );
2936
        $content = new Content(
2937
            [
2938
                'versionInfo' => $versionInfo,
2939
                'internalFields' => [],
2940
            ]
2941
        );
2942
2943
        $mockedService->expects($this->once())
2944
            ->method('loadContent')
2945
            ->with(
2946
                $this->equalTo(42),
2947
                $this->equalTo(null),
2948
                $this->equalTo(7)
2949
            )->will(
2950
                $this->returnValue($content)
2951
            );
2952
2953
        $permissionResolverMock->expects($this->once())
2954
            ->method('canUser')
2955
            ->with(
2956
                $this->equalTo('content'),
2957
                $this->equalTo('edit'),
2958
                $this->equalTo($content),
2959
                $this->isType('array')
2960
            )->will($this->returnValue(false));
2961
2962
        $mockedService->updateContent($versionInfo, $contentUpdateStruct);
2963
    }
2964
2965
    /**
2966
     * @param string $initialLanguageCode
2967
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2968
     * @param string[] $existingLanguages
2969
     *
2970
     * @return string[]
2971
     */
2972
    protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages)
2973
    {
2974
        $languageCodes = array_fill_keys($existingLanguages, true);
2975
        if ($initialLanguageCode !== null) {
2976
            $languageCodes[$initialLanguageCode] = true;
2977
        }
2978
2979
        foreach ($structFields as $field) {
2980
            if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) {
2981
                continue;
2982
            }
2983
2984
            $languageCodes[$field->languageCode] = true;
2985
        }
2986
2987
        return array_keys($languageCodes);
2988
    }
2989
2990
    /**
2991
     * @param string $initialLanguageCode
2992
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
2993
     * @param string $mainLanguageCode
2994
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
2995
     *
2996
     * @return array
2997
     */
2998
    protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions)
2999
    {
3000
        $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode;
3001
3002
        $mappedFieldDefinitions = [];
3003
        foreach ($fieldDefinitions as $fieldDefinition) {
3004
            $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition;
3005
        }
3006
3007
        $mappedStructFields = [];
3008
        foreach ($structFields as $structField) {
3009
            $identifier = $structField->fieldDefIdentifier;
3010
3011
            if ($structField->languageCode !== null) {
3012
                $languageCode = $structField->languageCode;
3013
            } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) {
3014
                $languageCode = $initialLanguageCode;
3015
            } else {
3016
                $languageCode = $mainLanguageCode;
3017
            }
3018
3019
            $mappedStructFields[$identifier][$languageCode] = (string)$structField->value;
3020
        }
3021
3022
        return $mappedStructFields;
3023
    }
3024
3025
    /**
3026
     * Returns full, possibly redundant array of field values, indexed by field definition
3027
     * identifier and language code.
3028
     *
3029
     * @param string $initialLanguageCode
3030
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3031
     * @param \eZ\Publish\Core\Repository\Values\Content\Content $content
3032
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3033
     * @param array $languageCodes
3034
     *
3035
     * @return array
3036
     */
3037
    protected function determineValuesForUpdate(
3038
        $initialLanguageCode,
3039
        array $structFields,
3040
        Content $content,
3041
        array $fieldDefinitions,
3042
        array $languageCodes
3043
    ) {
3044
        $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode;
3045
3046
        $mappedStructFields = $this->mapStructFieldsForUpdate(
3047
            $initialLanguageCode,
3048
            $structFields,
3049
            $mainLanguageCode,
3050
            $fieldDefinitions
3051
        );
3052
3053
        $values = [];
3054
3055
        foreach ($fieldDefinitions as $fieldDefinition) {
3056
            $identifier = $fieldDefinition->identifier;
3057
            foreach ($languageCodes as $languageCode) {
3058 View Code Duplication
                if (!$fieldDefinition->isTranslatable) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3059
                    if (isset($mappedStructFields[$identifier][$mainLanguageCode])) {
3060
                        $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode];
3061
                    } else {
3062
                        $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode];
3063
                    }
3064
                    continue;
3065
                }
3066
3067 View Code Duplication
                if (isset($mappedStructFields[$identifier][$languageCode])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3068
                    $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode];
3069
                    continue;
3070
                }
3071
3072
                if (isset($content->fields[$identifier][$languageCode])) {
3073
                    $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode];
3074
                    continue;
3075
                }
3076
3077
                $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue;
3078
            }
3079
        }
3080
3081
        return $this->stubValues($values);
3082
    }
3083
3084
    protected function stubValues(array $fieldValues)
3085
    {
3086
        foreach ($fieldValues as &$languageValues) {
3087
            foreach ($languageValues as &$value) {
3088
                $value = new ValueStub($value);
3089
            }
3090
        }
3091
3092
        return $fieldValues;
3093
    }
3094
3095
    /**
3096
     * Asserts that calling updateContent() with given API field set causes calling
3097
     * Handler::updateContent() with given SPI field set.
3098
     *
3099
     * @param string $initialLanguageCode
3100
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields
3101
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields
3102
     * @param \eZ\Publish\API\Repository\Values\Content\Field[] $existingFields
3103
     * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions
3104
     * @param bool $execute
3105
     *
3106
     * @return mixed
3107
     */
3108
    protected function assertForTestUpdateContentNonRedundantFieldSet(
3109
        $initialLanguageCode,
3110
        array $structFields,
3111
        array $spiFields,
3112
        array $existingFields,
3113
        array $fieldDefinitions,
3114
        $execute = true
3115
    ) {
3116
        $repositoryMock = $this->getRepositoryMock();
3117
        $permissionResolverMock = $this->getPermissionResolverMock();
3118
        $mockedService = $this->getPartlyMockedContentService(['internalLoadContent', 'internalLoadRelations']);
3119
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
3120
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
3121
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
3122
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
3123
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
3124
        $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...
3125
        $domainMapperMock = $this->getDomainMapperMock();
3126
        $relationProcessorMock = $this->getRelationProcessorMock();
3127
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
3128
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
3129
        $existingLanguageCodes = array_map(
3130
            function (Field $field) {
3131
                return $field->languageCode;
3132
            },
3133
            $existingFields
3134
        );
3135
        $languageCodes = $this->determineLanguageCodesForUpdate(
3136
            $initialLanguageCode,
3137
            $structFields,
3138
            $existingLanguageCodes
3139
        );
3140
        $versionInfo = new VersionInfo(
3141
            [
3142
                'contentInfo' => new ContentInfo(
3143
                    [
3144
                        'id' => 42,
3145
                        'contentTypeId' => 24,
3146
                        'mainLanguageCode' => 'eng-GB',
3147
                    ]
3148
                ),
3149
                'versionNo' => 7,
3150
                'languageCodes' => $existingLanguageCodes,
3151
                'status' => VersionInfo::STATUS_DRAFT,
3152
            ]
3153
        );
3154
        $content = new Content(
3155
            [
3156
                'versionInfo' => $versionInfo,
3157
                'internalFields' => $existingFields,
3158
            ]
3159
        );
3160
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
3161
3162
        $languageHandlerMock->expects($this->any())
3163
            ->method('loadByLanguageCode')
3164
            ->with($this->isType('string'))
3165
            ->will(
3166
                $this->returnCallback(
3167
                    function () {
3168
                        return new Language(['id' => 4242]);
3169
                    }
3170
                )
3171
            );
3172
3173
        $mockedService
3174
            ->method('internalLoadContent')
3175
            ->with(
3176
                $this->equalTo(42),
3177
                $this->equalTo(null),
3178
                $this->equalTo(7)
3179
            )->will(
3180
                $this->returnValue($content)
3181
            );
3182
3183
        $repositoryMock->expects($this->once())->method('beginTransaction');
3184
        $repositoryMock
3185
            ->method('canUser')
3186
            ->will($this->returnValue(true));
3187
3188
        $permissionResolverMock
3189
            ->method('canUser')
3190
            ->will($this->returnValue(true));
3191
3192
        $contentTypeServiceMock->expects($this->once())
3193
            ->method('loadContentType')
3194
            ->with($this->equalTo(24))
3195
            ->will($this->returnValue($contentType));
3196
3197
        $repositoryMock->expects($this->once())
3198
            ->method('getContentTypeService')
3199
            ->will($this->returnValue($contentTypeServiceMock));
3200
3201
        $repositoryMock->expects($this->once())
3202
            ->method('getCurrentUserReference')
3203
            ->will($this->returnValue(new UserReference(169)));
3204
3205
        $fieldTypeMock->expects($this->any())
3206
            ->method('acceptValue')
3207
            ->will(
3208
                $this->returnCallback(
3209
                    function ($valueString) {
3210
                        return new ValueStub($valueString);
3211
                    }
3212
                )
3213
            );
3214
3215
        $emptyValue = self::EMPTY_FIELD_VALUE;
3216
        $fieldTypeMock->expects($this->any())
3217
            ->method('toPersistenceValue')
3218
            ->will(
3219
                $this->returnCallback(
3220
                    function (ValueStub $value) {
3221
                        return (string)$value;
3222
                    }
3223
                )
3224
            );
3225
3226
        $fieldTypeMock->expects($this->any())
3227
            ->method('isEmptyValue')
3228
            ->will(
3229
                $this->returnCallback(
3230
                    function (ValueStub $value) use ($emptyValue) {
3231
                        return $emptyValue === (string)$value;
3232
                    }
3233
                )
3234
            );
3235
3236
        $fieldTypeMock->expects($this->any())
3237
            ->method('validate')
3238
            ->will($this->returnValue([]));
3239
3240
        $this->getFieldTypeRegistryMock()->expects($this->any())
3241
            ->method('getFieldType')
3242
            ->will($this->returnValue($fieldTypeMock));
3243
3244
        $relationProcessorMock
3245
            ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes)))
3246
            ->method('appendFieldRelations')
3247
            ->with(
3248
                $this->isType('array'),
3249
                $this->isType('array'),
3250
                $this->isInstanceOf(SPIFieldType::class),
3251
                $this->isInstanceOf(Value::class),
3252
                $this->anything()
3253
            );
3254
3255
        $values = $this->determineValuesForUpdate(
3256
            $initialLanguageCode,
3257
            $structFields,
3258
            $content,
3259
            $fieldDefinitions,
3260
            $languageCodes
3261
        );
3262
        $nameSchemaServiceMock->expects($this->once())
3263
            ->method('resolveNameSchema')
3264
            ->with(
3265
                $this->equalTo($content),
3266
                $this->equalTo($values),
3267
                $this->equalTo($languageCodes)
3268
            )->will($this->returnValue([]));
3269
3270
        $existingRelations = ['RELATIONS!!!'];
3271
        $mockedService
3272
            ->method('internalLoadRelations')
3273
            ->with($content->versionInfo)
3274
            ->will($this->returnValue($existingRelations));
3275
        $relationProcessorMock->expects($this->any())
3276
            ->method('processFieldRelations')
3277
            ->with(
3278
                $this->isType('array'),
3279
                $this->equalTo(42),
3280
                $this->isType('int'),
3281
                $this->equalTo($contentType),
3282
                $this->equalTo($existingRelations)
3283
            );
3284
3285
        $contentUpdateStruct = new ContentUpdateStruct(
3286
            [
3287
                'fields' => $structFields,
3288
                'initialLanguageCode' => $initialLanguageCode,
3289
            ]
3290
        );
3291
3292
        if ($execute) {
3293
            $spiContentUpdateStruct = new SPIContentUpdateStruct(
3294
                [
3295
                    'creatorId' => 169,
3296
                    'fields' => $spiFields,
3297
                    'modificationDate' => time(),
3298
                    'initialLanguageId' => 4242,
3299
                ]
3300
            );
3301
3302
            // During code coverage runs, timestamp might differ 1-3 seconds
3303
            $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct;
3304
            ++$spiContentUpdateStructTs1->modificationDate;
3305
3306
            $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1;
3307
            ++$spiContentUpdateStructTs2->modificationDate;
3308
3309
            $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2;
3310
            ++$spiContentUpdateStructTs3->modificationDate;
3311
3312
            $spiContent = new SPIContent(
3313
                [
3314
                    'versionInfo' => new SPIContent\VersionInfo(
3315
                        [
3316
                            'contentInfo' => new SPIContent\ContentInfo(['id' => 42]),
3317
                            'versionNo' => 7,
3318
                        ]
3319
                    ),
3320
                ]
3321
            );
3322
3323
            $contentHandlerMock->expects($this->once())
3324
                ->method('updateContent')
3325
                ->with(
3326
                    42,
3327
                    7,
3328
                    $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3)
3329
                )
3330
                ->will($this->returnValue($spiContent));
3331
3332
            $repositoryMock->expects($this->once())->method('commit');
3333
            $domainMapperMock
3334
                ->method('buildContentDomainObject')
3335
                ->with(
3336
                    $this->isInstanceOf(SPIContent::class),
3337
                    $this->isInstanceOf(APIContentType::class)
3338
                )
3339
                ->will($this->returnValue($content));
3340
3341
            $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
3342
        }
3343
3344
        return [$content->versionInfo, $contentUpdateStruct];
3345
    }
3346
3347
    public function providerForTestUpdateContentNonRedundantFieldSet1()
3348
    {
3349
        $spiFields = [
3350
            new SPIField(
3351
                [
3352
                    'id' => '100',
3353
                    'fieldDefinitionId' => 'fieldDefinitionId',
3354
                    'type' => 'fieldTypeIdentifier',
3355
                    'value' => 'newValue',
3356
                    'languageCode' => 'eng-GB',
3357
                    'versionNo' => 7,
3358
                ]
3359
            ),
3360
        ];
3361
3362
        return [
3363
            // With languages set
3364
            [
3365
                'eng-GB',
3366
                [
3367
                    new Field(
3368
                        [
3369
                            'fieldDefIdentifier' => 'identifier',
3370
                            'value' => 'newValue',
3371
                            'languageCode' => 'eng-GB',
3372
                        ]
3373
                    ),
3374
                ],
3375
                $spiFields,
3376
            ],
3377
            // Without languages set
3378
            [
3379
                null,
3380
                [
3381
                    new Field(
3382
                        [
3383
                            'fieldDefIdentifier' => 'identifier',
3384
                            'value' => 'newValue',
3385
                            'languageCode' => null,
3386
                        ]
3387
                    ),
3388
                ],
3389
                $spiFields,
3390
            ],
3391
            // Adding new language without fields
3392
            [
3393
                'eng-US',
3394
                [],
3395
                [],
3396
            ],
3397
        ];
3398
    }
3399
3400
    /**
3401
     * Test for the updateContent() method.
3402
     *
3403
     * Testing the simplest use case.
3404
     *
3405
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3406
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3407
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3408
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1
3409
     */
3410 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields)
3411
    {
3412
        $existingFields = [
3413
            new Field(
3414
                [
3415
                    'id' => '100',
3416
                    'fieldDefIdentifier' => 'identifier',
3417
                    'value' => 'initialValue',
3418
                    'languageCode' => 'eng-GB',
3419
                ]
3420
            ),
3421
        ];
3422
3423
        $fieldDefinitions = [
3424
            new FieldDefinition(
3425
                [
3426
                    'id' => 'fieldDefinitionId',
3427
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3428
                    'isTranslatable' => false,
3429
                    'identifier' => 'identifier',
3430
                    'isRequired' => false,
3431
                    'defaultValue' => 'defaultValue',
3432
                ]
3433
            ),
3434
        ];
3435
3436
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3437
            $initialLanguageCode,
3438
            $structFields,
3439
            $spiFields,
3440
            $existingFields,
3441
            $fieldDefinitions
3442
        );
3443
    }
3444
3445
    public function providerForTestUpdateContentNonRedundantFieldSet2()
3446
    {
3447
        $spiFields0 = [
3448
            new SPIField(
3449
                [
3450
                    'id' => '100',
3451
                    'fieldDefinitionId' => 'fieldDefinitionId',
3452
                    'type' => 'fieldTypeIdentifier',
3453
                    'value' => 'newValue',
3454
                    'languageCode' => 'eng-GB',
3455
                    'versionNo' => 7,
3456
                ]
3457
            ),
3458
        ];
3459
        $spiFields1 = [
3460
            new SPIField(
3461
                [
3462
                    'id' => null,
3463
                    'fieldDefinitionId' => 'fieldDefinitionId',
3464
                    'type' => 'fieldTypeIdentifier',
3465
                    'value' => 'newValue',
3466
                    'languageCode' => 'eng-US',
3467
                    'versionNo' => 7,
3468
                ]
3469
            ),
3470
        ];
3471
        $spiFields2 = [
3472
            new SPIField(
3473
                [
3474
                    'id' => 100,
3475
                    'fieldDefinitionId' => 'fieldDefinitionId',
3476
                    'type' => 'fieldTypeIdentifier',
3477
                    'value' => 'newValue2',
3478
                    'languageCode' => 'eng-GB',
3479
                    'versionNo' => 7,
3480
                ]
3481
            ),
3482
            new SPIField(
3483
                [
3484
                    'id' => null,
3485
                    'fieldDefinitionId' => 'fieldDefinitionId',
3486
                    'type' => 'fieldTypeIdentifier',
3487
                    'value' => 'newValue1',
3488
                    'languageCode' => 'eng-US',
3489
                    'versionNo' => 7,
3490
                ]
3491
            ),
3492
        ];
3493
3494
        return [
3495
            // 0. With languages set
3496
            [
3497
                'eng-GB',
3498
                [
3499
                    new Field(
3500
                        [
3501
                            'fieldDefIdentifier' => 'identifier',
3502
                            'value' => 'newValue',
3503
                            'languageCode' => 'eng-GB',
3504
                        ]
3505
                    ),
3506
                ],
3507
                $spiFields0,
3508
            ],
3509
            // 1. Without languages set
3510
            [
3511
                null,
3512
                [
3513
                    new Field(
3514
                        [
3515
                            'fieldDefIdentifier' => 'identifier',
3516
                            'value' => 'newValue',
3517
                            'languageCode' => null,
3518
                        ]
3519
                    ),
3520
                ],
3521
                $spiFields0,
3522
            ],
3523
            // 2. New language with language set
3524
            [
3525
                'eng-GB',
3526
                [
3527
                    new Field(
3528
                        [
3529
                            'fieldDefIdentifier' => 'identifier',
3530
                            'value' => 'newValue',
3531
                            'languageCode' => 'eng-US',
3532
                        ]
3533
                    ),
3534
                ],
3535
                $spiFields1,
3536
            ],
3537
            // 3. New language without language set
3538
            [
3539
                'eng-US',
3540
                [
3541
                    new Field(
3542
                        [
3543
                            'fieldDefIdentifier' => 'identifier',
3544
                            'value' => 'newValue',
3545
                            'languageCode' => null,
3546
                        ]
3547
                    ),
3548
                ],
3549
                $spiFields1,
3550
            ],
3551
            // 4. New language and existing language with language set
3552
            [
3553
                'eng-GB',
3554
                [
3555
                    new Field(
3556
                        [
3557
                            'fieldDefIdentifier' => 'identifier',
3558
                            'value' => 'newValue1',
3559
                            'languageCode' => 'eng-US',
3560
                        ]
3561
                    ),
3562
                    new Field(
3563
                        [
3564
                            'fieldDefIdentifier' => 'identifier',
3565
                            'value' => 'newValue2',
3566
                            'languageCode' => 'eng-GB',
3567
                        ]
3568
                    ),
3569
                ],
3570
                $spiFields2,
3571
            ],
3572
            // 5. New language and existing language without language set
3573
            [
3574
                'eng-US',
3575
                [
3576
                    new Field(
3577
                        [
3578
                            'fieldDefIdentifier' => 'identifier',
3579
                            'value' => 'newValue1',
3580
                            'languageCode' => null,
3581
                        ]
3582
                    ),
3583
                    new Field(
3584
                        [
3585
                            'fieldDefIdentifier' => 'identifier',
3586
                            'value' => 'newValue2',
3587
                            'languageCode' => 'eng-GB',
3588
                        ]
3589
                    ),
3590
                ],
3591
                $spiFields2,
3592
            ],
3593
            // 6. Adding new language without fields
3594
            [
3595
                'eng-US',
3596
                [],
3597
                [
3598
                    new SPIField(
3599
                        [
3600
                            'id' => null,
3601
                            'fieldDefinitionId' => 'fieldDefinitionId',
3602
                            'type' => 'fieldTypeIdentifier',
3603
                            'value' => 'defaultValue',
3604
                            'languageCode' => 'eng-US',
3605
                            'versionNo' => 7,
3606
                        ]
3607
                    ),
3608
                ],
3609
            ],
3610
        ];
3611
    }
3612
3613
    /**
3614
     * Test for the updateContent() method.
3615
     *
3616
     * Testing with translatable field.
3617
     *
3618
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3619
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3620
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3621
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2
3622
     */
3623 View Code Duplication
    public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields)
3624
    {
3625
        $existingFields = [
3626
            new Field(
3627
                [
3628
                    'id' => '100',
3629
                    'fieldDefIdentifier' => 'identifier',
3630
                    'value' => 'initialValue',
3631
                    'languageCode' => 'eng-GB',
3632
                ]
3633
            ),
3634
        ];
3635
3636
        $fieldDefinitions = [
3637
            new FieldDefinition(
3638
                [
3639
                    'id' => 'fieldDefinitionId',
3640
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3641
                    'isTranslatable' => true,
3642
                    'identifier' => 'identifier',
3643
                    'isRequired' => false,
3644
                    'defaultValue' => 'defaultValue',
3645
                ]
3646
            ),
3647
        ];
3648
3649
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3650
            $initialLanguageCode,
3651
            $structFields,
3652
            $spiFields,
3653
            $existingFields,
3654
            $fieldDefinitions
3655
        );
3656
    }
3657
3658
    public function providerForTestUpdateContentNonRedundantFieldSet3()
3659
    {
3660
        $spiFields0 = [
3661
            new SPIField(
3662
                [
3663
                    'id' => null,
3664
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3665
                    'type' => 'fieldTypeIdentifier',
3666
                    'value' => 'newValue1',
3667
                    'languageCode' => 'eng-US',
3668
                    'versionNo' => 7,
3669
                ]
3670
            ),
3671
        ];
3672
        $spiFields1 = [
3673
            new SPIField(
3674
                [
3675
                    'id' => 100,
3676
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3677
                    'type' => 'fieldTypeIdentifier',
3678
                    'value' => 'newValue2',
3679
                    'languageCode' => 'eng-GB',
3680
                    'versionNo' => 7,
3681
                ]
3682
            ),
3683
            new SPIField(
3684
                [
3685
                    'id' => null,
3686
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3687
                    'type' => 'fieldTypeIdentifier',
3688
                    'value' => 'newValue1',
3689
                    'languageCode' => 'eng-US',
3690
                    'versionNo' => 7,
3691
                ]
3692
            ),
3693
        ];
3694
        $spiFields2 = [
3695
            new SPIField(
3696
                [
3697
                    'id' => 100,
3698
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3699
                    'type' => 'fieldTypeIdentifier',
3700
                    'value' => 'newValue2',
3701
                    'languageCode' => 'eng-GB',
3702
                    'versionNo' => 7,
3703
                ]
3704
            ),
3705
            new SPIField(
3706
                [
3707
                    'id' => null,
3708
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3709
                    'type' => 'fieldTypeIdentifier',
3710
                    'value' => 'newValue1',
3711
                    'languageCode' => 'eng-US',
3712
                    'versionNo' => 7,
3713
                ]
3714
            ),
3715
            new SPIField(
3716
                [
3717
                    'id' => 101,
3718
                    'fieldDefinitionId' => 'fieldDefinitionId2',
3719
                    'type' => 'fieldTypeIdentifier',
3720
                    'value' => 'newValue3',
3721
                    'languageCode' => 'eng-GB',
3722
                    'versionNo' => 7,
3723
                ]
3724
            ),
3725
        ];
3726
        $spiFields3 = [
3727
            new SPIField(
3728
                [
3729
                    'id' => null,
3730
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3731
                    'type' => 'fieldTypeIdentifier',
3732
                    'value' => 'defaultValue1',
3733
                    'languageCode' => 'eng-US',
3734
                    'versionNo' => 7,
3735
                ]
3736
            ),
3737
        ];
3738
3739
        return [
3740
            // 0. ew language with language set
3741
            [
3742
                'eng-US',
3743
                [
3744
                    new Field(
3745
                        [
3746
                            'fieldDefIdentifier' => 'identifier1',
3747
                            'value' => 'newValue1',
3748
                            'languageCode' => 'eng-US',
3749
                        ]
3750
                    ),
3751
                ],
3752
                $spiFields0,
3753
            ],
3754
            // 1. New language without language set
3755
            [
3756
                'eng-US',
3757
                [
3758
                    new Field(
3759
                        [
3760
                            'fieldDefIdentifier' => 'identifier1',
3761
                            'value' => 'newValue1',
3762
                            'languageCode' => null,
3763
                        ]
3764
                    ),
3765
                ],
3766
                $spiFields0,
3767
            ],
3768
            // 2. New language and existing language with language set
3769
            [
3770
                'eng-US',
3771
                [
3772
                    new Field(
3773
                        [
3774
                            'fieldDefIdentifier' => 'identifier1',
3775
                            'value' => 'newValue1',
3776
                            'languageCode' => 'eng-US',
3777
                        ]
3778
                    ),
3779
                    new Field(
3780
                        [
3781
                            'fieldDefIdentifier' => 'identifier1',
3782
                            'value' => 'newValue2',
3783
                            'languageCode' => 'eng-GB',
3784
                        ]
3785
                    ),
3786
                ],
3787
                $spiFields1,
3788
            ],
3789
            // 3. New language and existing language without language set
3790
            [
3791
                'eng-US',
3792
                [
3793
                    new Field(
3794
                        [
3795
                            'fieldDefIdentifier' => 'identifier1',
3796
                            'value' => 'newValue1',
3797
                            'languageCode' => null,
3798
                        ]
3799
                    ),
3800
                    new Field(
3801
                        [
3802
                            'fieldDefIdentifier' => 'identifier1',
3803
                            'value' => 'newValue2',
3804
                            'languageCode' => 'eng-GB',
3805
                        ]
3806
                    ),
3807
                ],
3808
                $spiFields1,
3809
            ],
3810
            // 4. New language and existing language with untranslatable field, with language set
3811
            [
3812
                'eng-US',
3813
                [
3814
                    new Field(
3815
                        [
3816
                            'fieldDefIdentifier' => 'identifier1',
3817
                            'value' => 'newValue1',
3818
                            'languageCode' => 'eng-US',
3819
                        ]
3820
                    ),
3821
                    new Field(
3822
                        [
3823
                            'fieldDefIdentifier' => 'identifier1',
3824
                            'value' => 'newValue2',
3825
                            'languageCode' => 'eng-GB',
3826
                        ]
3827
                    ),
3828
                    new Field(
3829
                        [
3830
                            'fieldDefIdentifier' => 'identifier2',
3831
                            'value' => 'newValue3',
3832
                            'languageCode' => 'eng-GB',
3833
                        ]
3834
                    ),
3835
                ],
3836
                $spiFields2,
3837
            ],
3838
            // 5. New language and existing language with untranslatable field, without language set
3839
            [
3840
                'eng-US',
3841
                [
3842
                    new Field(
3843
                        [
3844
                            'fieldDefIdentifier' => 'identifier1',
3845
                            'value' => 'newValue1',
3846
                            'languageCode' => null,
3847
                        ]
3848
                    ),
3849
                    new Field(
3850
                        [
3851
                            'fieldDefIdentifier' => 'identifier1',
3852
                            'value' => 'newValue2',
3853
                            'languageCode' => 'eng-GB',
3854
                        ]
3855
                    ),
3856
                    new Field(
3857
                        [
3858
                            'fieldDefIdentifier' => 'identifier2',
3859
                            'value' => 'newValue3',
3860
                            'languageCode' => null,
3861
                        ]
3862
                    ),
3863
                ],
3864
                $spiFields2,
3865
            ],
3866
            // 6. Adding new language without fields
3867
            [
3868
                'eng-US',
3869
                [],
3870
                $spiFields3,
3871
            ],
3872
        ];
3873
    }
3874
3875
    /**
3876
     * Test for the updateContent() method.
3877
     *
3878
     * Testing with new language and untranslatable field.
3879
     *
3880
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
3881
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
3882
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
3883
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3
3884
     */
3885
    public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields)
3886
    {
3887
        $existingFields = [
3888
            new Field(
3889
                [
3890
                    'id' => '100',
3891
                    'fieldDefIdentifier' => 'identifier1',
3892
                    'value' => 'initialValue1',
3893
                    'languageCode' => 'eng-GB',
3894
                ]
3895
            ),
3896
            new Field(
3897
                [
3898
                    'id' => '101',
3899
                    'fieldDefIdentifier' => 'identifier2',
3900
                    'value' => 'initialValue2',
3901
                    'languageCode' => 'eng-GB',
3902
                ]
3903
            ),
3904
        ];
3905
3906
        $fieldDefinitions = [
3907
            new FieldDefinition(
3908
                [
3909
                    'id' => 'fieldDefinitionId1',
3910
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3911
                    'isTranslatable' => true,
3912
                    'identifier' => 'identifier1',
3913
                    'isRequired' => false,
3914
                    'defaultValue' => 'defaultValue1',
3915
                ]
3916
            ),
3917
            new FieldDefinition(
3918
                [
3919
                    'id' => 'fieldDefinitionId2',
3920
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
3921
                    'isTranslatable' => false,
3922
                    'identifier' => 'identifier2',
3923
                    'isRequired' => false,
3924
                    'defaultValue' => 'defaultValue2',
3925
                ]
3926
            ),
3927
        ];
3928
3929
        $this->assertForTestUpdateContentNonRedundantFieldSet(
3930
            $initialLanguageCode,
3931
            $structFields,
3932
            $spiFields,
3933
            $existingFields,
3934
            $fieldDefinitions
3935
        );
3936
    }
3937
3938
    public function providerForTestUpdateContentNonRedundantFieldSet4()
3939
    {
3940
        $spiFields0 = [
3941
            new SPIField(
3942
                [
3943
                    'id' => null,
3944
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3945
                    'type' => 'fieldTypeIdentifier',
3946
                    'value' => 'newValue1',
3947
                    'languageCode' => 'eng-US',
3948
                    'versionNo' => 7,
3949
                ]
3950
            ),
3951
        ];
3952
        $spiFields1 = [
3953
            new SPIField(
3954
                [
3955
                    'id' => 100,
3956
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3957
                    'type' => 'fieldTypeIdentifier',
3958
                    'value' => self::EMPTY_FIELD_VALUE,
3959
                    'languageCode' => 'eng-GB',
3960
                    'versionNo' => 7,
3961
                ]
3962
            ),
3963
            new SPIField(
3964
                [
3965
                    'id' => null,
3966
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3967
                    'type' => 'fieldTypeIdentifier',
3968
                    'value' => 'newValue1',
3969
                    'languageCode' => 'eng-US',
3970
                    'versionNo' => 7,
3971
                ]
3972
            ),
3973
        ];
3974
        $spiFields2 = [
3975
            new SPIField(
3976
                [
3977
                    'id' => 100,
3978
                    'fieldDefinitionId' => 'fieldDefinitionId1',
3979
                    'type' => 'fieldTypeIdentifier',
3980
                    'value' => self::EMPTY_FIELD_VALUE,
3981
                    'languageCode' => 'eng-GB',
3982
                    'versionNo' => 7,
3983
                ]
3984
            ),
3985
        ];
3986
3987
        return [
3988
            // 0. New translation with empty field by default
3989
            [
3990
                'eng-US',
3991
                [
3992
                    new Field(
3993
                        [
3994
                            'fieldDefIdentifier' => 'identifier1',
3995
                            'value' => 'newValue1',
3996
                            'languageCode' => 'eng-US',
3997
                        ]
3998
                    ),
3999
                ],
4000
                $spiFields0,
4001
            ],
4002
            // 1. New translation with empty field by default, without language set
4003
            [
4004
                'eng-US',
4005
                [
4006
                    new Field(
4007
                        [
4008
                            'fieldDefIdentifier' => 'identifier1',
4009
                            'value' => 'newValue1',
4010
                            'languageCode' => null,
4011
                        ]
4012
                    ),
4013
                ],
4014
                $spiFields0,
4015
            ],
4016
            // 2. New translation with empty field given
4017
            [
4018
                'eng-US',
4019
                [
4020
                    new Field(
4021
                        [
4022
                            'fieldDefIdentifier' => 'identifier1',
4023
                            'value' => 'newValue1',
4024
                            'languageCode' => 'eng-US',
4025
                        ]
4026
                    ),
4027
                    new Field(
4028
                        [
4029
                            'fieldDefIdentifier' => 'identifier2',
4030
                            'value' => self::EMPTY_FIELD_VALUE,
4031
                            'languageCode' => 'eng-US',
4032
                        ]
4033
                    ),
4034
                ],
4035
                $spiFields0,
4036
            ],
4037
            // 3. New translation with empty field given, without language set
4038
            [
4039
                'eng-US',
4040
                [
4041
                    new Field(
4042
                        [
4043
                            'fieldDefIdentifier' => 'identifier1',
4044
                            'value' => 'newValue1',
4045
                            'languageCode' => null,
4046
                        ]
4047
                    ),
4048
                    new Field(
4049
                        [
4050
                            'fieldDefIdentifier' => 'identifier2',
4051
                            'value' => self::EMPTY_FIELD_VALUE,
4052
                            'languageCode' => null,
4053
                        ]
4054
                    ),
4055
                ],
4056
                $spiFields0,
4057
            ],
4058
            // 4. Updating existing language with empty value
4059
            [
4060
                'eng-US',
4061
                [
4062
                    new Field(
4063
                        [
4064
                            'fieldDefIdentifier' => 'identifier1',
4065
                            'value' => 'newValue1',
4066
                            'languageCode' => 'eng-US',
4067
                        ]
4068
                    ),
4069
                    new Field(
4070
                        [
4071
                            'fieldDefIdentifier' => 'identifier1',
4072
                            'value' => self::EMPTY_FIELD_VALUE,
4073
                            'languageCode' => 'eng-GB',
4074
                        ]
4075
                    ),
4076
                ],
4077
                $spiFields1,
4078
            ],
4079
            // 5. Updating existing language with empty value, without language set
4080
            [
4081
                'eng-US',
4082
                [
4083
                    new Field(
4084
                        [
4085
                            'fieldDefIdentifier' => 'identifier1',
4086
                            'value' => 'newValue1',
4087
                            'languageCode' => null,
4088
                        ]
4089
                    ),
4090
                    new Field(
4091
                        [
4092
                            'fieldDefIdentifier' => 'identifier1',
4093
                            'value' => self::EMPTY_FIELD_VALUE,
4094
                            'languageCode' => 'eng-GB',
4095
                        ]
4096
                    ),
4097
                ],
4098
                $spiFields1,
4099
            ],
4100
            // 6. Updating existing language with empty value and adding new language with empty value
4101
            [
4102
                'eng-US',
4103
                [
4104
                    new Field(
4105
                        [
4106
                            'fieldDefIdentifier' => 'identifier1',
4107
                            'value' => self::EMPTY_FIELD_VALUE,
4108
                            'languageCode' => 'eng-US',
4109
                        ]
4110
                    ),
4111
                    new Field(
4112
                        [
4113
                            'fieldDefIdentifier' => 'identifier1',
4114
                            'value' => self::EMPTY_FIELD_VALUE,
4115
                            'languageCode' => 'eng-GB',
4116
                        ]
4117
                    ),
4118
                ],
4119
                $spiFields2,
4120
            ],
4121
            // 7. Updating existing language with empty value and adding new language with empty value,
4122
            // without language set
4123
            [
4124
                'eng-US',
4125
                [
4126
                    new Field(
4127
                        [
4128
                            'fieldDefIdentifier' => 'identifier1',
4129
                            'value' => self::EMPTY_FIELD_VALUE,
4130
                            'languageCode' => null,
4131
                        ]
4132
                    ),
4133
                    new Field(
4134
                        [
4135
                            'fieldDefIdentifier' => 'identifier1',
4136
                            'value' => self::EMPTY_FIELD_VALUE,
4137
                            'languageCode' => 'eng-GB',
4138
                        ]
4139
                    ),
4140
                ],
4141
                $spiFields2,
4142
            ],
4143
            // 8. Adding new language with no fields given
4144
            [
4145
                'eng-US',
4146
                [],
4147
                [],
4148
            ],
4149
            // 9. Adding new language with fields
4150
            [
4151
                'eng-US',
4152
                [
4153
                    new Field(
4154
                        [
4155
                            'fieldDefIdentifier' => 'identifier1',
4156
                            'value' => self::EMPTY_FIELD_VALUE,
4157
                            'languageCode' => 'eng-US',
4158
                        ]
4159
                    ),
4160
                ],
4161
                [],
4162
            ],
4163
            // 10. Adding new language with fields, without language set
4164
            [
4165
                'eng-US',
4166
                [
4167
                    new Field(
4168
                        [
4169
                            'fieldDefIdentifier' => 'identifier1',
4170
                            'value' => self::EMPTY_FIELD_VALUE,
4171
                            'languageCode' => null,
4172
                        ]
4173
                    ),
4174
                ],
4175
                [],
4176
            ],
4177
        ];
4178
    }
4179
4180
    /**
4181
     * Test for the updateContent() method.
4182
     *
4183
     * Testing with empty values.
4184
     *
4185
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4186
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4187
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4188
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4
4189
     */
4190
    public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields)
4191
    {
4192
        $existingFields = [
4193
            new Field(
4194
                [
4195
                    'id' => '100',
4196
                    'fieldDefIdentifier' => 'identifier1',
4197
                    'value' => 'initialValue1',
4198
                    'languageCode' => 'eng-GB',
4199
                ]
4200
            ),
4201
            new Field(
4202
                [
4203
                    'id' => '101',
4204
                    'fieldDefIdentifier' => 'identifier2',
4205
                    'value' => 'initialValue2',
4206
                    'languageCode' => 'eng-GB',
4207
                ]
4208
            ),
4209
        ];
4210
4211
        $fieldDefinitions = [
4212
            new FieldDefinition(
4213
                [
4214
                    'id' => 'fieldDefinitionId1',
4215
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4216
                    'isTranslatable' => true,
4217
                    'identifier' => 'identifier1',
4218
                    'isRequired' => false,
4219
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4220
                ]
4221
            ),
4222
            new FieldDefinition(
4223
                [
4224
                    'id' => 'fieldDefinitionId2',
4225
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4226
                    'isTranslatable' => true,
4227
                    'identifier' => 'identifier2',
4228
                    'isRequired' => false,
4229
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4230
                ]
4231
            ),
4232
        ];
4233
4234
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4235
            $initialLanguageCode,
4236
            $structFields,
4237
            $spiFields,
4238
            $existingFields,
4239
            $fieldDefinitions
4240
        );
4241
    }
4242
4243
    /**
4244
     * @todo add first field empty
4245
     *
4246
     * @return array
4247
     */
4248
    public function providerForTestUpdateContentNonRedundantFieldSetComplex()
4249
    {
4250
        $spiFields0 = [
4251
            new SPIField(
4252
                [
4253
                    'id' => 100,
4254
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4255
                    'type' => 'fieldTypeIdentifier',
4256
                    'value' => 'newValue1-eng-GB',
4257
                    'languageCode' => 'eng-GB',
4258
                    'versionNo' => 7,
4259
                ]
4260
            ),
4261
            new SPIField(
4262
                [
4263
                    'id' => null,
4264
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4265
                    'type' => 'fieldTypeIdentifier',
4266
                    'value' => 'newValue4',
4267
                    'languageCode' => 'eng-US',
4268
                    'versionNo' => 7,
4269
                ]
4270
            ),
4271
        ];
4272
        $spiFields1 = [
4273
            new SPIField(
4274
                [
4275
                    'id' => 100,
4276
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4277
                    'type' => 'fieldTypeIdentifier',
4278
                    'value' => 'newValue1-eng-GB',
4279
                    'languageCode' => 'eng-GB',
4280
                    'versionNo' => 7,
4281
                ]
4282
            ),
4283
            new SPIField(
4284
                [
4285
                    'id' => null,
4286
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4287
                    'type' => 'fieldTypeIdentifier',
4288
                    'value' => 'newValue2',
4289
                    'languageCode' => 'eng-US',
4290
                    'versionNo' => 7,
4291
                ]
4292
            ),
4293
            new SPIField(
4294
                [
4295
                    'id' => null,
4296
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4297
                    'type' => 'fieldTypeIdentifier',
4298
                    'value' => 'defaultValue4',
4299
                    'languageCode' => 'eng-US',
4300
                    'versionNo' => 7,
4301
                ]
4302
            ),
4303
        ];
4304
        $spiFields2 = [
4305
            new SPIField(
4306
                [
4307
                    'id' => 100,
4308
                    'fieldDefinitionId' => 'fieldDefinitionId1',
4309
                    'type' => 'fieldTypeIdentifier',
4310
                    'value' => 'newValue1-eng-GB',
4311
                    'languageCode' => 'eng-GB',
4312
                    'versionNo' => 7,
4313
                ]
4314
            ),
4315
            new SPIField(
4316
                [
4317
                    'id' => null,
4318
                    'fieldDefinitionId' => 'fieldDefinitionId2',
4319
                    'type' => 'fieldTypeIdentifier',
4320
                    'value' => 'newValue2',
4321
                    'languageCode' => 'eng-US',
4322
                    'versionNo' => 7,
4323
                ]
4324
            ),
4325
            new SPIField(
4326
                [
4327
                    'id' => null,
4328
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4329
                    'type' => 'fieldTypeIdentifier',
4330
                    'value' => 'defaultValue4',
4331
                    'languageCode' => 'ger-DE',
4332
                    'versionNo' => 7,
4333
                ]
4334
            ),
4335
            new SPIField(
4336
                [
4337
                    'id' => null,
4338
                    'fieldDefinitionId' => 'fieldDefinitionId4',
4339
                    'type' => 'fieldTypeIdentifier',
4340
                    'value' => 'defaultValue4',
4341
                    'languageCode' => 'eng-US',
4342
                    'versionNo' => 7,
4343
                ]
4344
            ),
4345
        ];
4346
4347
        return [
4348
            // 0. Add new language and update existing
4349
            [
4350
                'eng-US',
4351
                [
4352
                    new Field(
4353
                        [
4354
                            'fieldDefIdentifier' => 'identifier4',
4355
                            'value' => 'newValue4',
4356
                            'languageCode' => 'eng-US',
4357
                        ]
4358
                    ),
4359
                    new Field(
4360
                        [
4361
                            'fieldDefIdentifier' => 'identifier1',
4362
                            'value' => 'newValue1-eng-GB',
4363
                            'languageCode' => 'eng-GB',
4364
                        ]
4365
                    ),
4366
                ],
4367
                $spiFields0,
4368
            ],
4369
            // 1. Add new language and update existing, without language set
4370
            [
4371
                'eng-US',
4372
                [
4373
                    new Field(
4374
                        [
4375
                            'fieldDefIdentifier' => 'identifier4',
4376
                            'value' => 'newValue4',
4377
                            'languageCode' => null,
4378
                        ]
4379
                    ),
4380
                    new Field(
4381
                        [
4382
                            'fieldDefIdentifier' => 'identifier1',
4383
                            'value' => 'newValue1-eng-GB',
4384
                            'languageCode' => 'eng-GB',
4385
                        ]
4386
                    ),
4387
                ],
4388
                $spiFields0,
4389
            ],
4390
            // 2. Add new language and update existing variant
4391
            [
4392
                'eng-US',
4393
                [
4394
                    new Field(
4395
                        [
4396
                            'fieldDefIdentifier' => 'identifier2',
4397
                            'value' => 'newValue2',
4398
                            'languageCode' => 'eng-US',
4399
                        ]
4400
                    ),
4401
                    new Field(
4402
                        [
4403
                            'fieldDefIdentifier' => 'identifier1',
4404
                            'value' => 'newValue1-eng-GB',
4405
                            'languageCode' => 'eng-GB',
4406
                        ]
4407
                    ),
4408
                ],
4409
                $spiFields1,
4410
            ],
4411
            // 3. Add new language and update existing variant, without language set
4412
            [
4413
                'eng-US',
4414
                [
4415
                    new Field(
4416
                        [
4417
                            'fieldDefIdentifier' => 'identifier2',
4418
                            'value' => 'newValue2',
4419
                            'languageCode' => null,
4420
                        ]
4421
                    ),
4422
                    new Field(
4423
                        [
4424
                            'fieldDefIdentifier' => 'identifier1',
4425
                            'value' => 'newValue1-eng-GB',
4426
                            'languageCode' => 'eng-GB',
4427
                        ]
4428
                    ),
4429
                ],
4430
                $spiFields1,
4431
            ],
4432
            // 4. Update with multiple languages
4433
            [
4434
                'ger-DE',
4435
                [
4436
                    new Field(
4437
                        [
4438
                            'fieldDefIdentifier' => 'identifier2',
4439
                            'value' => 'newValue2',
4440
                            'languageCode' => 'eng-US',
4441
                        ]
4442
                    ),
4443
                    new Field(
4444
                        [
4445
                            'fieldDefIdentifier' => 'identifier1',
4446
                            'value' => 'newValue1-eng-GB',
4447
                            'languageCode' => 'eng-GB',
4448
                        ]
4449
                    ),
4450
                ],
4451
                $spiFields2,
4452
            ],
4453
            // 5. Update with multiple languages without language set
4454
            [
4455
                'ger-DE',
4456
                [
4457
                    new Field(
4458
                        [
4459
                            'fieldDefIdentifier' => 'identifier2',
4460
                            'value' => 'newValue2',
4461
                            'languageCode' => 'eng-US',
4462
                        ]
4463
                    ),
4464
                    new Field(
4465
                        [
4466
                            'fieldDefIdentifier' => 'identifier1',
4467
                            'value' => 'newValue1-eng-GB',
4468
                            'languageCode' => null,
4469
                        ]
4470
                    ),
4471
                ],
4472
                $spiFields2,
4473
            ],
4474
        ];
4475
    }
4476
4477
    protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex()
4478
    {
4479
        $existingFields = [
4480
            new Field(
4481
                [
4482
                    'id' => '100',
4483
                    'fieldDefIdentifier' => 'identifier1',
4484
                    'value' => 'initialValue1',
4485
                    'languageCode' => 'eng-GB',
4486
                ]
4487
            ),
4488
            new Field(
4489
                [
4490
                    'id' => '101',
4491
                    'fieldDefIdentifier' => 'identifier2',
4492
                    'value' => 'initialValue2',
4493
                    'languageCode' => 'eng-GB',
4494
                ]
4495
            ),
4496
            new Field(
4497
                [
4498
                    'id' => '102',
4499
                    'fieldDefIdentifier' => 'identifier3',
4500
                    'value' => 'initialValue3',
4501
                    'languageCode' => 'eng-GB',
4502
                ]
4503
            ),
4504
            new Field(
4505
                [
4506
                    'id' => '103',
4507
                    'fieldDefIdentifier' => 'identifier4',
4508
                    'value' => 'initialValue4',
4509
                    'languageCode' => 'eng-GB',
4510
                ]
4511
            ),
4512
        ];
4513
4514
        $fieldDefinitions = [
4515
            new FieldDefinition(
4516
                [
4517
                    'id' => 'fieldDefinitionId1',
4518
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4519
                    'isTranslatable' => false,
4520
                    'identifier' => 'identifier1',
4521
                    'isRequired' => false,
4522
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4523
                ]
4524
            ),
4525
            new FieldDefinition(
4526
                [
4527
                    'id' => 'fieldDefinitionId2',
4528
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4529
                    'isTranslatable' => true,
4530
                    'identifier' => 'identifier2',
4531
                    'isRequired' => false,
4532
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4533
                ]
4534
            ),
4535
            new FieldDefinition(
4536
                [
4537
                    'id' => 'fieldDefinitionId3',
4538
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4539
                    'isTranslatable' => false,
4540
                    'identifier' => 'identifier3',
4541
                    'isRequired' => false,
4542
                    'defaultValue' => 'defaultValue3',
4543
                ]
4544
            ),
4545
            new FieldDefinition(
4546
                [
4547
                    'id' => 'fieldDefinitionId4',
4548
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4549
                    'isTranslatable' => true,
4550
                    'identifier' => 'identifier4',
4551
                    'isRequired' => false,
4552
                    'defaultValue' => 'defaultValue4',
4553
                ]
4554
            ),
4555
        ];
4556
4557
        return [$existingFields, $fieldDefinitions];
4558
    }
4559
4560
    /**
4561
     * Test for the updateContent() method.
4562
     *
4563
     * Testing more complex cases.
4564
     *
4565
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4566
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4567
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4568
     * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex
4569
     */
4570
    public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields)
4571
    {
4572
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
4573
4574
        $this->assertForTestUpdateContentNonRedundantFieldSet(
4575
            $initialLanguageCode,
4576
            $structFields,
4577
            $spiFields,
4578
            $existingFields,
4579
            $fieldDefinitions
4580
        );
4581
    }
4582
4583 View Code Duplication
    public function providerForTestUpdateContentWithInvalidLanguage()
4584
    {
4585
        return [
4586
            [
4587
                'eng-GB',
4588
                [
4589
                    new Field(
4590
                        [
4591
                            'fieldDefIdentifier' => 'identifier',
4592
                            'value' => 'newValue',
4593
                            'languageCode' => 'Klingon',
4594
                        ]
4595
                    ),
4596
                ],
4597
            ],
4598
            [
4599
                'Klingon',
4600
                [
4601
                    new Field(
4602
                        [
4603
                            'fieldDefIdentifier' => 'identifier',
4604
                            'value' => 'newValue',
4605
                            'languageCode' => 'eng-GB',
4606
                        ]
4607
                    ),
4608
                ],
4609
            ],
4610
        ];
4611
    }
4612
4613
    /**
4614
     * Test for the updateContent() method.
4615
     *
4616
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4617
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4618
     * @dataProvider providerForTestUpdateContentWithInvalidLanguage
4619
     * @expectedException \eZ\Publish\API\Repository\Exceptions\NotFoundException
4620
     * @expectedExceptionMessage Could not find 'Language' with identifier 'Klingon'
4621
     */
4622
    public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields)
4623
    {
4624
        $repositoryMock = $this->getRepositoryMock();
0 ignored issues
show
Unused Code introduced by
$repositoryMock 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...
4625
        $permissionResolverMock = $this->getPermissionResolverMock();
4626
        $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContent']);
4627
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4628
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4629
        $versionInfo = new VersionInfo(
4630
            [
4631
                'contentInfo' => new ContentInfo(
4632
                    [
4633
                        'id' => 42,
4634
                        'contentTypeId' => 24,
4635
                        'mainLanguageCode' => 'eng-GB',
4636
                    ]
4637
                ),
4638
                'versionNo' => 7,
4639
                'languageCodes' => ['eng-GB'],
4640
                'status' => VersionInfo::STATUS_DRAFT,
4641
            ]
4642
        );
4643
        $content = new Content(
4644
            [
4645
                'versionInfo' => $versionInfo,
4646
                'internalFields' => [],
4647
            ]
4648
        );
4649
4650
        $languageHandlerMock->expects($this->any())
4651
            ->method('loadByLanguageCode')
4652
            ->with($this->isType('string'))
4653
            ->will(
4654
                $this->returnCallback(
4655 View Code Duplication
                    function ($languageCode) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4656
                        if ($languageCode === 'Klingon') {
4657
                            throw new NotFoundException('Language', 'Klingon');
4658
                        }
4659
4660
                        return new Language(['id' => 4242]);
4661
                    }
4662
                )
4663
            );
4664
4665
        $mockedService
4666
            ->method('loadContent')
4667
            ->with(
4668
                $this->equalTo(42),
4669
                $this->equalTo(null),
4670
                $this->equalTo(7)
4671
            )->will(
4672
                $this->returnValue($content)
4673
            );
4674
4675
        $mockedService
4676
            ->method('internalLoadContent')
4677
            ->will(
4678
                $this->returnValue($content)
4679
            );
4680
4681
        $permissionResolverMock
4682
            ->method('canUser')
4683
            ->with(
4684
                $this->equalTo('content'),
4685
                $this->equalTo('edit'),
4686
                $this->equalTo($content),
4687
                $this->isType('array')
4688
            )->will($this->returnValue(true));
4689
4690
        $contentUpdateStruct = new ContentUpdateStruct(
4691
            [
4692
                'fields' => $structFields,
4693
                'initialLanguageCode' => $initialLanguageCode,
4694
            ]
4695
        );
4696
4697
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4698
    }
4699
4700
    protected function assertForUpdateContentContentValidationException(
4701
        $initialLanguageCode,
4702
        $structFields,
4703
        $fieldDefinitions = []
4704
    ) {
4705
        $repositoryMock = $this->getRepositoryMock();
4706
        $permissionResolverMock = $this->getPermissionResolverMock();
4707
        $mockedService = $this->getPartlyMockedContentService(['internalLoadContent', 'loadContent']);
4708
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4709
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4710
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4711
        $versionInfo = new VersionInfo(
4712
            [
4713
                'contentInfo' => new ContentInfo(
4714
                    [
4715
                        'id' => 42,
4716
                        'contentTypeId' => 24,
4717
                        'mainLanguageCode' => 'eng-GB',
4718
                    ]
4719
                ),
4720
                'versionNo' => 7,
4721
                'languageCodes' => ['eng-GB'],
4722
                'status' => VersionInfo::STATUS_DRAFT,
4723
            ]
4724
        );
4725
        $content = new Content(
4726
            [
4727
                'versionInfo' => $versionInfo,
4728
                'internalFields' => [],
4729
            ]
4730
        );
4731
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4732
4733
        $languageHandlerMock->expects($this->any())
4734
            ->method('loadByLanguageCode')
4735
            ->with($this->isType('string'))
4736
            ->will(
4737
                $this->returnCallback(
4738 View Code Duplication
                    function ($languageCode) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
4739
                        if ($languageCode === 'Klingon') {
4740
                            throw new NotFoundException('Language', 'Klingon');
4741
                        }
4742
4743
                        return new Language(['id' => 4242]);
4744
                    }
4745
                )
4746
            );
4747
4748
        $mockedService
4749
            ->method('loadContent')
4750
            ->with(
4751
                $this->equalTo(42),
4752
                $this->equalTo(null),
4753
                $this->equalTo(7)
4754
            )->will(
4755
                $this->returnValue($content)
4756
            );
4757
4758
        $mockedService
4759
            ->method('internalLoadContent')
4760
            ->will(
4761
                $this->returnValue($content)
4762
            );
4763
4764
        $permissionResolverMock->expects($this->once())
4765
            ->method('canUser')
4766
            ->with(
4767
                $this->equalTo('content'),
4768
                $this->equalTo('edit'),
4769
                $this->equalTo($content),
4770
                $this->isType('array')
4771
            )->will($this->returnValue(true));
4772
4773
        $contentTypeServiceMock->expects($this->once())
4774
            ->method('loadContentType')
4775
            ->with($this->equalTo(24))
4776
            ->will($this->returnValue($contentType));
4777
4778
        $repositoryMock->expects($this->once())
4779
            ->method('getContentTypeService')
4780
            ->will($this->returnValue($contentTypeServiceMock));
4781
4782
        $contentUpdateStruct = new ContentUpdateStruct(
4783
            [
4784
                'fields' => $structFields,
4785
                'initialLanguageCode' => $initialLanguageCode,
4786
            ]
4787
        );
4788
4789
        $mockedService->updateContent($content->versionInfo, $contentUpdateStruct);
4790
    }
4791
4792 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition()
4793
    {
4794
        return [
4795
            [
4796
                'eng-GB',
4797
                [
4798
                    new Field(
4799
                        [
4800
                            'fieldDefIdentifier' => 'identifier',
4801
                            'value' => 'newValue',
4802
                            'languageCode' => 'eng-GB',
4803
                        ]
4804
                    ),
4805
                ],
4806
            ],
4807
        ];
4808
    }
4809
4810
    /**
4811
     * Test for the updateContent() method.
4812
     *
4813
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4814
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4815
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4816
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition
4817
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentValidationException
4818
     * @expectedExceptionMessage Field definition 'identifier' does not exist in given ContentType
4819
     */
4820
    public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields)
4821
    {
4822
        $this->assertForUpdateContentContentValidationException(
4823
            $initialLanguageCode,
4824
            $structFields,
4825
            []
4826
        );
4827
    }
4828
4829 View Code Duplication
    public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation()
4830
    {
4831
        return [
4832
            [
4833
                'eng-US',
4834
                [
4835
                    new Field(
4836
                        [
4837
                            'fieldDefIdentifier' => 'identifier',
4838
                            'value' => 'newValue',
4839
                            'languageCode' => 'eng-US',
4840
                        ]
4841
                    ),
4842
                ],
4843
            ],
4844
        ];
4845
    }
4846
4847
    /**
4848
     * Test for the updateContent() method.
4849
     *
4850
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
4851
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
4852
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
4853
     * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation
4854
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentValidationException
4855
     * @expectedExceptionMessage A value is set for non translatable field definition 'identifier' with language 'eng-US'
4856
     */
4857 View Code Duplication
    public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields)
4858
    {
4859
        $fieldDefinitions = [
4860
            new FieldDefinition(
4861
                [
4862
                    'id' => 'fieldDefinitionId1',
4863
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
4864
                    'isTranslatable' => false,
4865
                    'identifier' => 'identifier',
4866
                    'isRequired' => false,
4867
                    'defaultValue' => self::EMPTY_FIELD_VALUE,
4868
                ]
4869
            ),
4870
        ];
4871
4872
        $this->assertForUpdateContentContentValidationException(
4873
            $initialLanguageCode,
4874
            $structFields,
4875
            $fieldDefinitions
4876
        );
4877
    }
4878
4879
    public function assertForTestUpdateContentRequiredField(
4880
        $initialLanguageCode,
4881
        $structFields,
4882
        $existingFields,
4883
        $fieldDefinitions
4884
    ) {
4885
        $repositoryMock = $this->getRepositoryMock();
4886
        $permissionResolver = $this->getPermissionResolverMock();
4887
        $mockedService = $this->getPartlyMockedContentService(['internalLoadContent', 'loadContent']);
4888
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
4889
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
4890
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
4891
        $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...
4892
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
4893
        $existingLanguageCodes = array_map(
4894
            function (Field $field) {
4895
                return $field->languageCode;
4896
            },
4897
            $existingFields
4898
        );
4899
        $versionInfo = new VersionInfo(
4900
            [
4901
                'contentInfo' => new ContentInfo(
4902
                    [
4903
                        'id' => 42,
4904
                        'contentTypeId' => 24,
4905
                        'mainLanguageCode' => 'eng-GB',
4906
                    ]
4907
                ),
4908
                'versionNo' => 7,
4909
                'languageCodes' => $existingLanguageCodes,
4910
                'status' => VersionInfo::STATUS_DRAFT,
4911
            ]
4912
        );
4913
        $content = new Content(
4914
            [
4915
                'versionInfo' => $versionInfo,
4916
                'internalFields' => $existingFields,
4917
            ]
4918
        );
4919
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
4920
4921
        $languageHandlerMock->expects($this->any())
4922
            ->method('loadByLanguageCode')
4923
            ->with($this->isType('string'))
4924
            ->will(
4925
                $this->returnCallback(
4926
                    function () {
4927
                        return new Language(['id' => 4242]);
4928
                    }
4929
                )
4930
            );
4931
4932
        $mockedService
4933
            ->method('loadContent')
4934
            ->with(
4935
                $this->equalTo(42),
4936
                $this->equalTo(null),
4937
                $this->equalTo(7)
4938
            )->will(
4939
                $this->returnValue($content)
4940
            );
4941
4942
        $mockedService
4943
            ->method('internalLoadContent')
4944
            ->will(
4945
                $this->returnValue($content)
4946
            );
4947
4948
        $permissionResolver->expects($this->once())
4949
            ->method('canUser')
4950
            ->with(
4951
                $this->equalTo('content'),
4952
                $this->equalTo('edit'),
4953
                $this->equalTo($content),
4954
                $this->isType('array')
4955
            )->will($this->returnValue(true));
4956
4957
        $contentTypeServiceMock->expects($this->once())
4958
            ->method('loadContentType')
4959
            ->with($this->equalTo(24))
4960
            ->will($this->returnValue($contentType));
4961
4962
        $repositoryMock->expects($this->once())
4963
            ->method('getContentTypeService')
4964
            ->will($this->returnValue($contentTypeServiceMock));
4965
4966
        $fieldTypeMock->expects($this->any())
4967
            ->method('acceptValue')
4968
            ->will(
4969
                $this->returnCallback(
4970
                    function ($valueString) {
4971
                        return new ValueStub($valueString);
4972
                    }
4973
                )
4974
            );
4975
4976
        $emptyValue = self::EMPTY_FIELD_VALUE;
4977
        $fieldTypeMock->expects($this->any())
4978
            ->method('isEmptyValue')
4979
            ->will(
4980
                $this->returnCallback(
4981
                    function (ValueStub $value) use ($emptyValue) {
4982
                        return $emptyValue === (string)$value;
4983
                    }
4984
                )
4985
            );
4986
4987
        $fieldTypeMock->expects($this->any())
4988
            ->method('validate')
4989
            ->with(
4990
                $this->isInstanceOf(APIFieldDefinition::class),
4991
                $this->isInstanceOf(Value::class)
4992
            );
4993
4994
        $this->getFieldTypeRegistryMock()->expects($this->any())
4995
            ->method('getFieldType')
4996
            ->will($this->returnValue($fieldTypeMock));
4997
4998
        $contentUpdateStruct = new ContentUpdateStruct(
4999
            [
5000
                'fields' => $structFields,
5001
                'initialLanguageCode' => $initialLanguageCode,
5002
            ]
5003
        );
5004
5005
        return [$content->versionInfo, $contentUpdateStruct];
5006
    }
5007
5008 View Code Duplication
    public function providerForTestUpdateContentRequiredField()
5009
    {
5010
        return [
5011
            [
5012
                'eng-US',
5013
                [
5014
                    new Field(
5015
                        [
5016
                            'fieldDefIdentifier' => 'identifier',
5017
                            'value' => self::EMPTY_FIELD_VALUE,
5018
                            'languageCode' => null,
5019
                        ]
5020
                    ),
5021
                ],
5022
                'identifier',
5023
                'eng-US',
5024
            ],
5025
        ];
5026
    }
5027
5028
    /**
5029
     * Test for the updateContent() method.
5030
     *
5031
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5032
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5033
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5034
     * @dataProvider providerForTestUpdateContentRequiredField
5035
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
5036
     */
5037
    public function testUpdateContentRequiredField(
5038
        $initialLanguageCode,
5039
        $structFields,
5040
        $identifier,
5041
        $languageCode
5042
    ) {
5043
        $existingFields = [
5044
            new Field(
5045
                [
5046
                    'id' => '100',
5047
                    'fieldDefIdentifier' => 'identifier',
5048
                    'value' => 'initialValue',
5049
                    'languageCode' => 'eng-GB',
5050
                ]
5051
            ),
5052
        ];
5053
        $fieldDefinitions = [
5054
            new FieldDefinition(
5055
                [
5056
                    'id' => 'fieldDefinitionId',
5057
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5058
                    'isTranslatable' => true,
5059
                    'identifier' => 'identifier',
5060
                    'isRequired' => true,
5061
                    'defaultValue' => 'defaultValue',
5062
                ]
5063
            ),
5064
        ];
5065
        list($versionInfo, $contentUpdateStruct) =
5066
            $this->assertForTestUpdateContentRequiredField(
5067
                $initialLanguageCode,
5068
                $structFields,
5069
                $existingFields,
5070
                $fieldDefinitions
5071
            );
5072
5073
        try {
5074
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5075
        } catch (ContentValidationException $e) {
5076
            $this->assertEquals(
5077
                "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty",
5078
                $e->getMessage()
5079
            );
5080
5081
            throw $e;
5082
        }
5083
    }
5084
5085
    public function assertForTestUpdateContentThrowsContentFieldValidationException(
5086
        $initialLanguageCode,
5087
        $structFields,
5088
        $existingFields,
5089
        $fieldDefinitions
5090
    ) {
5091
        $repositoryMock = $this->getRepositoryMock();
5092
        $permissionResolverMock = $this->getPermissionResolverMock();
5093
        $mockedService = $this->getPartlyMockedContentService(['internalLoadContent', 'loadContent']);
5094
        /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */
5095
        $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler();
5096
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5097
        $fieldTypeMock = $this->createMock(SPIFieldType::class);
5098
        $existingLanguageCodes = array_map(
5099
            function (Field $field) {
5100
                return $field->languageCode;
5101
            },
5102
            $existingFields
5103
        );
5104
        $languageCodes = $this->determineLanguageCodesForUpdate(
5105
            $initialLanguageCode,
5106
            $structFields,
5107
            $existingLanguageCodes
5108
        );
5109
        $versionInfo = new VersionInfo(
5110
            [
5111
                'contentInfo' => new ContentInfo(
5112
                    [
5113
                        'id' => 42,
5114
                        'contentTypeId' => 24,
5115
                        'mainLanguageCode' => 'eng-GB',
5116
                    ]
5117
                ),
5118
                'versionNo' => 7,
5119
                'languageCodes' => $existingLanguageCodes,
5120
                'status' => VersionInfo::STATUS_DRAFT,
5121
            ]
5122
        );
5123
        $content = new Content(
5124
            [
5125
                'versionInfo' => $versionInfo,
5126
                'internalFields' => $existingFields,
5127
            ]
5128
        );
5129
        $contentType = new ContentType(['fieldDefinitions' => $fieldDefinitions]);
5130
5131
        $languageHandlerMock->expects($this->any())
5132
            ->method('loadByLanguageCode')
5133
            ->with($this->isType('string'))
5134
            ->will(
5135
                $this->returnCallback(
5136
                    function () {
5137
                        return new Language(['id' => 4242]);
5138
                    }
5139
                )
5140
            );
5141
5142
        $mockedService
5143
            ->method('internalLoadContent')
5144
            ->will(
5145
                $this->returnValue($content)
5146
            );
5147
5148
        $mockedService
5149
            ->method('loadContent')
5150
            ->with(
5151
                $this->equalTo(42),
5152
                $this->equalTo(null),
5153
                $this->equalTo(7)
5154
            )
5155
            ->will(
5156
                $this->returnValue($content)
5157
            );
5158
5159
        $permissionResolverMock
5160
            ->method('canUser')
5161
            ->will($this->returnValue(true));
5162
5163
        $contentTypeServiceMock->expects($this->once())
5164
            ->method('loadContentType')
5165
            ->with($this->equalTo(24))
5166
            ->will($this->returnValue($contentType));
5167
5168
        $repositoryMock->expects($this->once())
5169
            ->method('getContentTypeService')
5170
            ->will($this->returnValue($contentTypeServiceMock));
5171
5172
        $fieldValues = $this->determineValuesForUpdate(
5173
            $initialLanguageCode,
5174
            $structFields,
5175
            $content,
5176
            $fieldDefinitions,
5177
            $languageCodes
5178
        );
5179
        $allFieldErrors = [];
5180
        $emptyValue = self::EMPTY_FIELD_VALUE;
5181
5182
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5183
            ->method('acceptValue')
5184
            ->will(
5185
                $this->returnCallback(
5186
                    function ($valueString) {
5187
                        return new ValueStub($valueString);
5188
                    }
5189
                )
5190
            );
5191
5192
        $fieldTypeMock->expects($this->exactly(count($fieldValues) * count($languageCodes)))
5193
            ->method('isEmptyValue')
5194
            ->will(
5195
                $this->returnCallback(
5196
                    function (ValueStub $value) use ($emptyValue) {
5197
                        return $emptyValue === (string)$value;
5198
                    }
5199
                )
5200
            );
5201
5202
        $fieldTypeMock
5203
            ->expects($this->any())
5204
            ->method('validate')
5205
            ->willReturnArgument(1);
5206
5207
        $this->getFieldTypeRegistryMock()->expects($this->any())
5208
            ->method('getFieldType')
5209
            ->will($this->returnValue($fieldTypeMock));
5210
5211
        $contentUpdateStruct = new ContentUpdateStruct(
5212
            [
5213
                'fields' => $structFields,
5214
                'initialLanguageCode' => $initialLanguageCode,
5215
            ]
5216
        );
5217
5218
        return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors];
5219
    }
5220
5221
    public function providerForTestUpdateContentThrowsContentFieldValidationException()
5222
    {
5223
        $allFieldErrors = [
5224
            [
5225
                'fieldDefinitionId1' => [
5226
                    'eng-GB' => 'newValue1-eng-GB',
5227
                    'eng-US' => 'newValue1-eng-GB',
5228
                ],
5229
                'fieldDefinitionId2' => [
5230
                    'eng-GB' => 'initialValue2',
5231
                ],
5232
                'fieldDefinitionId3' => [
5233
                    'eng-GB' => 'initialValue3',
5234
                    'eng-US' => 'initialValue3',
5235
                ],
5236
                'fieldDefinitionId4' => [
5237
                    'eng-GB' => 'initialValue4',
5238
                    'eng-US' => 'newValue4',
5239
                ],
5240
            ],
5241
            [
5242
                'fieldDefinitionId1' => [
5243
                    'eng-GB' => 'newValue1-eng-GB',
5244
                    'eng-US' => 'newValue1-eng-GB',
5245
                ],
5246
                'fieldDefinitionId2' => [
5247
                    'eng-GB' => 'initialValue2',
5248
                ],
5249
                'fieldDefinitionId3' => [
5250
                    'eng-GB' => 'initialValue3',
5251
                    'eng-US' => 'initialValue3',
5252
                ],
5253
                'fieldDefinitionId4' => [
5254
                    'eng-GB' => 'initialValue4',
5255
                    'eng-US' => 'newValue4',
5256
                ],
5257
            ],
5258
            [
5259
                'fieldDefinitionId1' => [
5260
                    'eng-GB' => 'newValue1-eng-GB',
5261
                    'eng-US' => 'newValue1-eng-GB',
5262
                ],
5263
                'fieldDefinitionId2' => [
5264
                    'eng-GB' => 'initialValue2',
5265
                    'eng-US' => 'newValue2',
5266
                ],
5267
                'fieldDefinitionId3' => [
5268
                    'eng-GB' => 'initialValue3',
5269
                    'eng-US' => 'initialValue3',
5270
                ],
5271
                'fieldDefinitionId4' => [
5272
                    'eng-GB' => 'initialValue4',
5273
                    'eng-US' => 'defaultValue4',
5274
                ],
5275
            ],
5276
            [
5277
                'fieldDefinitionId1' => [
5278
                    'eng-GB' => 'newValue1-eng-GB',
5279
                    'eng-US' => 'newValue1-eng-GB',
5280
                ],
5281
                'fieldDefinitionId2' => [
5282
                    'eng-GB' => 'initialValue2',
5283
                    'eng-US' => 'newValue2',
5284
                ],
5285
                'fieldDefinitionId3' => [
5286
                    'eng-GB' => 'initialValue3',
5287
                    'eng-US' => 'initialValue3',
5288
                ],
5289
                'fieldDefinitionId4' => [
5290
                    'eng-GB' => 'initialValue4',
5291
                    'eng-US' => 'defaultValue4',
5292
                ],
5293
            ],
5294
            [
5295
                'fieldDefinitionId1' => [
5296
                    'eng-GB' => 'newValue1-eng-GB',
5297
                    'ger-DE' => 'newValue1-eng-GB',
5298
                    'eng-US' => 'newValue1-eng-GB',
5299
                ],
5300
                'fieldDefinitionId2' => [
5301
                    'eng-GB' => 'initialValue2',
5302
                    'eng-US' => 'newValue2',
5303
                ],
5304
                'fieldDefinitionId3' => [
5305
                    'eng-GB' => 'initialValue3',
5306
                    'ger-DE' => 'initialValue3',
5307
                    'eng-US' => 'initialValue3',
5308
                ],
5309
                'fieldDefinitionId4' => [
5310
                    'eng-GB' => 'initialValue4',
5311
                    'eng-US' => 'defaultValue4',
5312
                    'ger-DE' => 'defaultValue4',
5313
                ],
5314
            ],
5315
            [
5316
                'fieldDefinitionId1' => [
5317
                    'eng-US' => 'newValue1-eng-GB',
5318
                    'ger-DE' => 'newValue1-eng-GB',
5319
                ],
5320
                'fieldDefinitionId2' => [
5321
                    'eng-US' => 'newValue2',
5322
                ],
5323
                'fieldDefinitionId3' => [
5324
                    'ger-DE' => 'initialValue3',
5325
                    'eng-US' => 'initialValue3',
5326
                ],
5327
                'fieldDefinitionId4' => [
5328
                    'ger-DE' => 'defaultValue4',
5329
                    'eng-US' => 'defaultValue4',
5330
                ],
5331
            ],
5332
        ];
5333
5334
        $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex();
5335
        $count = count($data);
5336
        for ($i = 0; $i < $count; ++$i) {
5337
            $data[$i][] = $allFieldErrors[$i];
5338
        }
5339
5340
        return $data;
5341
    }
5342
5343
    /**
5344
     * Test for the updateContent() method.
5345
     *
5346
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5347
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5348
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5349
     * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException
5350
     * @expectedException \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException
5351
     * @expectedExceptionMessage Content fields did not validate
5352
     */
5353
    public function testUpdateContentThrowsContentFieldValidationException($initialLanguageCode, $structFields, $spiField, $allFieldErrors)
0 ignored issues
show
Unused Code introduced by
The parameter $spiField is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
5354
    {
5355
        list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex();
5356
        list($versionInfo, $contentUpdateStruct) =
5357
            $this->assertForTestUpdateContentThrowsContentFieldValidationException(
5358
                $initialLanguageCode,
5359
                $structFields,
5360
                $existingFields,
5361
                $fieldDefinitions
5362
            );
5363
5364
        try {
5365
            $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5366
        } catch (ContentFieldValidationException $e) {
5367
            $this->assertEquals($allFieldErrors, $e->getFieldErrors());
5368
            throw $e;
5369
        }
5370
    }
5371
5372
    /**
5373
     * Test for the updateContent() method.
5374
     *
5375
     * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate
5376
     * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate
5377
     * @covers \eZ\Publish\Core\Repository\ContentService::updateContent
5378
     * @expectedException \Exception
5379
     * @expectedExceptionMessage Store failed
5380
     */
5381
    public function testUpdateContentTransactionRollback()
5382
    {
5383
        $existingFields = [
5384
            new Field(
5385
                [
5386
                    'id' => '100',
5387
                    'fieldDefIdentifier' => 'identifier',
5388
                    'value' => 'initialValue',
5389
                    'languageCode' => 'eng-GB',
5390
                ]
5391
            ),
5392
        ];
5393
5394
        $fieldDefinitions = [
5395
            new FieldDefinition(
5396
                [
5397
                    'id' => 'fieldDefinitionId',
5398
                    'fieldTypeIdentifier' => 'fieldTypeIdentifier',
5399
                    'isTranslatable' => false,
5400
                    'identifier' => 'identifier',
5401
                    'isRequired' => false,
5402
                    'defaultValue' => 'defaultValue',
5403
                ]
5404
            ),
5405
        ];
5406
5407
        // Setup a simple case that will pass
5408
        list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet(
5409
            'eng-US',
5410
            [],
5411
            [],
5412
            $existingFields,
5413
            $fieldDefinitions,
5414
            // Do not execute test
5415
            false
5416
        );
5417
5418
        $repositoryMock = $this->getRepositoryMock();
5419
        $repositoryMock->expects($this->never())->method('commit');
5420
        $repositoryMock->expects($this->once())->method('rollback');
5421
5422
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5423
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5424
        $contentHandlerMock->expects($this->once())
5425
            ->method('updateContent')
5426
            ->with(
5427
                $this->anything(),
5428
                $this->anything(),
5429
                $this->anything()
5430
            )->will($this->throwException(new \Exception('Store failed')));
5431
5432
        // Execute
5433
        $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct);
5434
    }
5435
5436
    /**
5437
     * Test for the copyContent() method.
5438
     *
5439
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5440
     * @expectedException \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
5441
     */
5442
    public function testCopyContentThrowsUnauthorizedException()
5443
    {
5444
        $repository = $this->getRepositoryMock();
5445
        $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']);
5446
        $contentInfo = $this->createMock(APIContentInfo::class);
5447
        $locationCreateStruct = new LocationCreateStruct();
5448
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5449
        $locationServiceMock = $this->getLocationServiceMock();
5450
5451
        $repository->expects($this->once())
5452
            ->method('getLocationService')
5453
            ->will($this->returnValue($locationServiceMock))
5454
        ;
5455
5456
        $locationServiceMock->expects($this->once())
5457
            ->method('loadLocation')
5458
            ->with(
5459
                $locationCreateStruct->parentLocationId
5460
            )
5461
            ->will($this->returnValue($location))
5462
        ;
5463
5464
        $contentInfo->expects($this->any())
5465
            ->method('__get')
5466
            ->with('sectionId')
5467
            ->will($this->returnValue(42));
5468
5469
        $repository->expects($this->once())
5470
            ->method('canUser')
5471
            ->with(
5472
                'content',
5473
                'create',
5474
                $contentInfo,
5475
                [$location]
5476
            )
5477
            ->will($this->returnValue(false));
5478
5479
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */
5480
        $contentService->copyContent($contentInfo, $locationCreateStruct);
5481
    }
5482
5483
    /**
5484
     * Test for the copyContent() method.
5485
     *
5486
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5487
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5488
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5489
     */
5490
    public function testCopyContent()
5491
    {
5492
        $repositoryMock = $this->getRepositoryMock();
5493
        $contentService = $this->getPartlyMockedContentService([
5494
            'internalLoadContentInfo',
5495
            'internalLoadContent',
5496
            'getUnixTimestamp',
5497
        ]);
5498
        $locationServiceMock = $this->getLocationServiceMock();
5499
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5500
        $locationCreateStruct = new LocationCreateStruct();
5501
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5502
        $user = $this->getStubbedUser(14);
5503
5504
        $permissionResolverMock = $this->getPermissionResolverMock();
5505
5506
        $permissionResolverMock
5507
            ->method('getCurrentUserReference')
5508
            ->willReturn($user);
5509
5510
        $repositoryMock
5511
            ->method('getPermissionResolver')
5512
            ->willReturn($permissionResolverMock);
5513
5514
        $repositoryMock->expects($this->exactly(3))
5515
            ->method('getLocationService')
5516
            ->will($this->returnValue($locationServiceMock));
5517
5518
        $locationServiceMock->expects($this->once())
5519
            ->method('loadLocation')
5520
            ->with($locationCreateStruct->parentLocationId)
5521
            ->will($this->returnValue($location))
5522
        ;
5523
5524
        $contentInfoMock->expects($this->any())
5525
            ->method('__get')
5526
            ->will(
5527
                $this->returnValueMap(
5528
                    [
5529
                        ['isHidden', true],
5530
                        ['id', 42],
5531
                    ]
5532
                )
5533
            );
5534
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5535
5536
        $versionInfoMock->expects($this->any())
5537
            ->method('__get')
5538
            ->will(
5539
                $this->returnValueMap(
5540
                    [
5541
                        ['versionNo', 123],
5542
                    ]
5543
                )
5544
            );
5545
5546
        $versionInfoMock->expects($this->once())
5547
            ->method('isDraft')
5548
            ->willReturn(true);
5549
5550
        $versionInfoMock->expects($this->once())
5551
            ->method('getContentInfo')
5552
            ->will($this->returnValue($contentInfoMock));
5553
5554
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5555
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5556
        $domainMapperMock = $this->getDomainMapperMock();
5557
5558
        $repositoryMock->expects($this->once())->method('beginTransaction');
5559
        $repositoryMock->expects($this->once())->method('commit');
5560
        $repositoryMock
5561
            ->method('canUser')
5562
            ->willReturnMap(
5563
                [
5564
                    ['content', 'create', $contentInfoMock, [$location], true],
5565
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5566
                ]
5567
            );
5568
5569
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5570
        $spiVersionInfo = new SPIVersionInfo(
5571
            [
5572
                'contentInfo' => $spiContentInfo,
5573
                'creationDate' => 123456,
5574
            ]
5575
        );
5576
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5577
        $contentHandlerMock->expects($this->once())
5578
            ->method('copy')
5579
            ->with(42, null)
5580
            ->will($this->returnValue($spiContent));
5581
5582
        $this->mockGetDefaultObjectStates();
5583
        $this->mockSetDefaultObjectStates();
5584
5585
        $domainMapperMock->expects($this->once())
5586
            ->method('buildVersionInfoDomainObject')
5587
            ->with($spiVersionInfo)
5588
            ->will($this->returnValue($versionInfoMock));
5589
5590
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5591
        $content = $this->mockPublishVersion(123456, 126666, true);
5592
        $locationServiceMock->expects($this->once())
5593
            ->method('createLocation')
5594
            ->with(
5595
                $content->getVersionInfo()->getContentInfo(),
5596
                $locationCreateStruct
5597
            );
5598
5599
        $contentService->expects($this->once())
5600
            ->method('internalLoadContent')
5601
            ->with(
5602
                $content->id
5603
            )
5604
            ->will($this->returnValue($content));
5605
5606
        $contentService->expects($this->once())
5607
            ->method('getUnixTimestamp')
5608
            ->will($this->returnValue(126666));
5609
5610
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5611
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5612
    }
5613
5614
    /**
5615
     * Test for the copyContent() method.
5616
     *
5617
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5618
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5619
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5620
     */
5621
    public function testCopyContentWithVersionInfo()
5622
    {
5623
        $repositoryMock = $this->getRepositoryMock();
5624
        $contentService = $this->getPartlyMockedContentService([
5625
            'internalLoadContentInfo',
5626
            'internalLoadContent',
5627
            'getUnixTimestamp',
5628
        ]);
5629
        $locationServiceMock = $this->getLocationServiceMock();
5630
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5631
        $locationCreateStruct = new LocationCreateStruct();
5632
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5633
        $user = $this->getStubbedUser(14);
5634
5635
        $permissionResolverMock = $this->getPermissionResolverMock();
5636
5637
        $permissionResolverMock
5638
            ->method('getCurrentUserReference')
5639
            ->willReturn($user);
5640
5641
        $repositoryMock
5642
            ->method('getPermissionResolver')
5643
            ->willReturn($permissionResolverMock);
5644
5645
        $repositoryMock->expects($this->exactly(3))
5646
            ->method('getLocationService')
5647
            ->will($this->returnValue($locationServiceMock));
5648
5649
        $locationServiceMock->expects($this->once())
5650
            ->method('loadLocation')
5651
            ->with($locationCreateStruct->parentLocationId)
5652
            ->will($this->returnValue($location))
5653
        ;
5654
5655
        $contentInfoMock->expects($this->any())
5656
            ->method('__get')
5657
            ->will(
5658
                $this->returnValueMap([
5659
                    ['isHidden', true],
5660
                    ['id', 42],
5661
                ])
5662
            );
5663
5664
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5665
5666
        $versionInfoMock->expects($this->any())
5667
            ->method('__get')
5668
            ->will(
5669
                $this->returnValueMap(
5670
                    [
5671
                        ['versionNo', 123],
5672
                    ]
5673
                )
5674
            );
5675
        $versionInfoMock->expects($this->once())
5676
            ->method('isDraft')
5677
            ->willReturn(true);
5678
        $versionInfoMock->expects($this->once())
5679
            ->method('getContentInfo')
5680
            ->will($this->returnValue($contentInfoMock));
5681
5682
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5683
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5684
        $domainMapperMock = $this->getDomainMapperMock();
5685
5686
        $repositoryMock->expects($this->once())->method('beginTransaction');
5687
        $repositoryMock->expects($this->once())->method('commit');
5688
        $repositoryMock
5689
            ->method('canUser')
5690
            ->willReturnMap(
5691
                [
5692
                    ['content', 'create', $contentInfoMock, [$location], true],
5693
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5694
                ]
5695
            );
5696
5697
        $spiContentInfo = new SPIContentInfo(['id' => 42]);
5698
        $spiVersionInfo = new SPIVersionInfo(
5699
            [
5700
                'contentInfo' => $spiContentInfo,
5701
                'creationDate' => 123456,
5702
            ]
5703
        );
5704
        $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]);
5705
        $contentHandlerMock->expects($this->once())
5706
            ->method('copy')
5707
            ->with(42, 123)
5708
            ->will($this->returnValue($spiContent));
5709
5710
        $this->mockGetDefaultObjectStates();
5711
        $this->mockSetDefaultObjectStates();
5712
5713
        $domainMapperMock->expects($this->once())
5714
            ->method('buildVersionInfoDomainObject')
5715
            ->with($spiVersionInfo)
5716
            ->will($this->returnValue($versionInfoMock));
5717
5718
        /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */
5719
        $content = $this->mockPublishVersion(123456, 126666, true);
5720
        $locationServiceMock->expects($this->once())
5721
            ->method('createLocation')
5722
            ->with(
5723
                $content->getVersionInfo()->getContentInfo(),
5724
                $locationCreateStruct
5725
            );
5726
5727
        $contentService->expects($this->once())
5728
            ->method('internalLoadContent')
5729
            ->with(
5730
                $content->id
5731
            )
5732
            ->will($this->returnValue($content));
5733
5734
        $contentService->expects($this->once())
5735
            ->method('getUnixTimestamp')
5736
            ->will($this->returnValue(126666));
5737
5738
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5739
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock);
5740
    }
5741
5742
    /**
5743
     * Test for the copyContent() method.
5744
     *
5745
     * @covers \eZ\Publish\Core\Repository\ContentService::copyContent
5746
     * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates
5747
     * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion
5748
     * @expectedException \Exception
5749
     * @expectedExceptionMessage Handler threw an exception
5750
     */
5751
    public function testCopyContentWithRollback()
5752
    {
5753
        $repositoryMock = $this->getRepositoryMock();
5754
        $contentService = $this->getPartlyMockedContentService();
5755
        /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5756
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5757
        $locationCreateStruct = new LocationCreateStruct();
5758
        $location = new Location(['id' => $locationCreateStruct->parentLocationId]);
5759
        $locationServiceMock = $this->getLocationServiceMock();
5760
        $user = $this->getStubbedUser(14);
5761
5762
        $permissionResolverMock = $this->getPermissionResolverMock();
5763
5764
        $permissionResolverMock
5765
            ->method('getCurrentUserReference')
5766
            ->willReturn($user);
5767
5768
        $repositoryMock
5769
            ->method('getPermissionResolver')
5770
            ->willReturn($permissionResolverMock);
5771
5772
        $repositoryMock->expects($this->once())
5773
            ->method('getLocationService')
5774
            ->will($this->returnValue($locationServiceMock))
5775
        ;
5776
5777
        $locationServiceMock->expects($this->once())
5778
            ->method('loadLocation')
5779
            ->with($locationCreateStruct->parentLocationId)
5780
            ->will($this->returnValue($location))
5781
        ;
5782
5783
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5784
        $contentInfoMock->expects($this->any())
5785
            ->method('__get')
5786
            ->with('id')
5787
            ->will($this->returnValue(42));
5788
5789
        $this->mockGetDefaultObjectStates();
5790
5791
        $repositoryMock->expects($this->once())->method('beginTransaction');
5792
        $repositoryMock->expects($this->once())->method('rollback');
5793
        $repositoryMock
5794
            ->method('canUser')
5795
            ->willReturnMap(
5796
                [
5797
                    ['content', 'create', $contentInfoMock, [$location], true],
5798
                    ['content', 'manage_locations', $contentInfoMock, [$location], true],
5799
                ]
5800
            );
5801
5802
        $contentHandlerMock->expects($this->once())
5803
            ->method('copy')
5804
            ->with(42, null)
5805
            ->will($this->throwException(new Exception('Handler threw an exception')));
5806
5807
        /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */
5808
        $contentService->copyContent($contentInfoMock, $locationCreateStruct, null);
5809
    }
5810
5811
    /**
5812
     * Reusable method for setting exceptions on buildContentDomainObject usage.
5813
     *
5814
     * Plain usage as in when content type is loaded directly.
5815
     *
5816
     * @param \eZ\Publish\SPI\Persistence\Content $spiContent
5817
     * @param array $translations
5818
     * @param bool $useAlwaysAvailable
5819
     *
5820
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Values\Content\Content
5821
     */
5822
    private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null)
5823
    {
5824
        $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId;
5825
        $contentTypeServiceMock = $this->getContentTypeServiceMock();
5826
        $repositoryMock = $this->getRepositoryMock();
5827
5828
        $contentType = new ContentType([
5829
            'id' => $contentTypeId,
5830
            'fieldDefinitions' => [],
5831
        ]);
5832
5833
        $repositoryMock->expects($this->once())
5834
            ->method('getContentTypeService')
5835
            ->willReturn($contentTypeServiceMock);
5836
5837
        $contentTypeServiceMock->expects($this->once())
5838
            ->method('loadContentType')
5839
            ->with($this->equalTo($contentTypeId))
5840
            ->willReturn($contentType);
5841
5842
        $content = $this->createMock(APIContent::class);
5843
5844
        $this->getDomainMapperMock()
5845
            ->expects($this->once())
5846
            ->method('buildContentDomainObject')
5847
            ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable)
5848
            ->willReturn($content);
5849
5850
        return $content;
5851
    }
5852
5853
    protected function mockGetDefaultObjectStates()
5854
    {
5855
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5856
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5857
5858
        $objectStateGroups = [
5859
            new SPIObjectStateGroup(['id' => 10]),
5860
            new SPIObjectStateGroup(['id' => 20]),
5861
        ];
5862
5863
        /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5864
        $objectStateHandlerMock->expects($this->once())
5865
            ->method('loadAllGroups')
5866
            ->will($this->returnValue($objectStateGroups));
5867
5868
        $objectStateHandlerMock->expects($this->at(1))
5869
            ->method('loadObjectStates')
5870
            ->with($this->equalTo(10))
5871
            ->will(
5872
                $this->returnValue(
5873
                    [
5874
                        new SPIObjectState(['id' => 11, 'groupId' => 10]),
5875
                        new SPIObjectState(['id' => 12, 'groupId' => 10]),
5876
                    ]
5877
                )
5878
            );
5879
5880
        $objectStateHandlerMock->expects($this->at(2))
5881
            ->method('loadObjectStates')
5882
            ->with($this->equalTo(20))
5883
            ->will(
5884
                $this->returnValue(
5885
                    [
5886
                        new SPIObjectState(['id' => 21, 'groupId' => 20]),
5887
                        new SPIObjectState(['id' => 22, 'groupId' => 20]),
5888
                    ]
5889
                )
5890
            );
5891
    }
5892
5893
    protected function mockSetDefaultObjectStates()
5894
    {
5895
        /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */
5896
        $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler();
5897
5898
        $defaultObjectStates = [
5899
            new SPIObjectState(['id' => 11, 'groupId' => 10]),
5900
            new SPIObjectState(['id' => 21, 'groupId' => 20]),
5901
        ];
5902
        foreach ($defaultObjectStates as $index => $objectState) {
5903
            $objectStateHandlerMock->expects($this->at($index + 3))
5904
                ->method('setContentState')
5905
                ->with(
5906
                    42,
5907
                    $objectState->groupId,
5908
                    $objectState->id
5909
                );
5910
        }
5911
    }
5912
5913
    /**
5914
     * @param int|null $publicationDate
5915
     * @param int|null $modificationDate
5916
     * @param bool $isHidden
5917
     *
5918
     * @return \eZ\Publish\API\Repository\Values\Content\Content
5919
     */
5920
    protected function mockPublishVersion($publicationDate = null, $modificationDate = null, $isHidden = false)
5921
    {
5922
        $versionInfoMock = $this->createMock(APIVersionInfo::class);
5923
        $contentInfoMock = $this->createMock(APIContentInfo::class);
5924
        /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */
5925
        $contentHandlerMock = $this->getPersistenceMock()->contentHandler();
5926
        $metadataUpdateStruct = new SPIMetadataUpdateStruct();
5927
5928
        $spiContent = new SPIContent([
5929
            'versionInfo' => new VersionInfo([
5930
                    'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]),
5931
            ]),
5932
        ]);
5933
5934
        $contentMock = $this->mockBuildContentDomainObject($spiContent);
5935
        $contentMock->expects($this->any())
5936
            ->method('__get')
5937
            ->will(
5938
                $this->returnValueMap(
5939
                    [
5940
                        ['id', 42],
5941
                        ['contentInfo', $contentInfoMock],
5942
                        ['versionInfo', $versionInfoMock],
5943
                    ]
5944
                )
5945
            );
5946
        $contentMock->expects($this->any())
5947
            ->method('getVersionInfo')
5948
            ->will($this->returnValue($versionInfoMock));
5949
        $versionInfoMock->expects($this->any())
5950
            ->method('getContentInfo')
5951
            ->will($this->returnValue($contentInfoMock));
5952
        $versionInfoMock->expects($this->any())
5953
            ->method('__get')
5954
            ->will(
5955
                $this->returnValueMap(
5956
                    [
5957
                        ['languageCodes', ['eng-GB']],
5958
                    ]
5959
                )
5960
            );
5961
        $contentInfoMock->expects($this->any())
5962
            ->method('__get')
5963
            ->will(
5964
                $this->returnValueMap(
5965
                    [
5966
                        ['alwaysAvailable', true],
5967
                        ['mainLanguageCode', 'eng-GB'],
5968
                    ]
5969
                )
5970
            );
5971
5972
        $currentTime = time();
5973
        if ($publicationDate === null && $versionInfoMock->versionNo === 1) {
5974
            $publicationDate = $currentTime;
5975
        }
5976
5977
        // Account for 1 second of test execution time
5978
        $metadataUpdateStruct->publicationDate = $publicationDate;
5979
        $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime;
5980
        $metadataUpdateStruct->isHidden = $isHidden;
5981
5982
        $contentHandlerMock->expects($this->once())
5983
            ->method('publish')
5984
            ->with(
5985
                42,
5986
                123,
5987
                $metadataUpdateStruct
5988
            )
5989
            ->will($this->returnValue($spiContent));
5990
5991
        /* @var \eZ\Publish\API\Repository\Values\Content\Content $contentMock */
5992
        $this->mockPublishUrlAliasesForContent($contentMock);
5993
5994
        return $contentMock;
5995
    }
5996
5997
    /**
5998
     * @param \eZ\Publish\API\Repository\Values\Content\Content $content
5999
     */
6000
    protected function mockPublishUrlAliasesForContent(APIContent $content)
6001
    {
6002
        $nameSchemaServiceMock = $this->getNameSchemaServiceMock();
6003
        /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */
6004
        $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler();
6005
        $locationServiceMock = $this->getLocationServiceMock();
6006
        $location = $this->createMock(APILocation::class);
6007
6008
        $location->expects($this->at(0))
6009
            ->method('__get')
6010
            ->with('id')
6011
            ->will($this->returnValue(123));
6012
        $location->expects($this->at(1))
6013
            ->method('__get')
6014
            ->with('parentLocationId')
6015
            ->will($this->returnValue(456));
6016
6017
        $urlAliasNames = ['eng-GB' => 'hello'];
6018
        $nameSchemaServiceMock->expects($this->once())
6019
            ->method('resolveUrlAliasSchema')
6020
            ->with($content)
6021
            ->will($this->returnValue($urlAliasNames));
6022
6023
        $locationServiceMock->expects($this->once())
6024
            ->method('loadLocations')
6025
            ->with($content->getVersionInfo()->getContentInfo())
6026
            ->will($this->returnValue([$location]));
6027
6028
        $urlAliasHandlerMock->expects($this->once())
6029
            ->method('publishUrlAliasForLocation')
6030
            ->with(123, 456, 'hello', 'eng-GB', true, true);
6031
6032
        $location->expects($this->at(2))
6033
            ->method('__get')
6034
            ->with('id')
6035
            ->will($this->returnValue(123));
6036
6037
        $location->expects($this->at(3))
6038
            ->method('__get')
6039
            ->with('parentLocationId')
6040
            ->will($this->returnValue(456));
6041
6042
        $urlAliasHandlerMock->expects($this->once())
6043
            ->method('archiveUrlAliasesForDeletedTranslations')
6044
            ->with(123, 456, ['eng-GB']);
6045
    }
6046
6047
    protected $domainMapperMock;
6048
6049
    /**
6050
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\DomainMapper
6051
     */
6052
    protected function getDomainMapperMock()
6053
    {
6054
        if (!isset($this->domainMapperMock)) {
6055
            $this->domainMapperMock = $this->createMock(DomainMapper::class);
6056
        }
6057
6058
        return $this->domainMapperMock;
6059
    }
6060
6061
    protected $relationProcessorMock;
6062
6063
    /**
6064
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\RelationProcessor
6065
     */
6066
    protected function getRelationProcessorMock()
6067
    {
6068
        if (!isset($this->relationProcessorMock)) {
6069
            $this->relationProcessorMock = $this->createMock(RelationProcessor::class);
6070
        }
6071
6072
        return $this->relationProcessorMock;
6073
    }
6074
6075
    protected $nameSchemaServiceMock;
6076
6077
    /**
6078
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService
6079
     */
6080
    protected function getNameSchemaServiceMock()
6081
    {
6082
        if (!isset($this->nameSchemaServiceMock)) {
6083
            $this->nameSchemaServiceMock = $this->createMock(NameSchemaService::class);
6084
        }
6085
6086
        return $this->nameSchemaServiceMock;
6087
    }
6088
6089
    protected $contentTypeServiceMock;
6090
6091
    /**
6092
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentTypeService
6093
     */
6094
    protected function getContentTypeServiceMock()
6095
    {
6096
        if (!isset($this->contentTypeServiceMock)) {
6097
            $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class);
6098
        }
6099
6100
        return $this->contentTypeServiceMock;
6101
    }
6102
6103
    protected $locationServiceMock;
6104
6105
    /**
6106
     * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\LocationService
6107
     */
6108
    protected function getLocationServiceMock()
6109
    {
6110
        if (!isset($this->locationServiceMock)) {
6111
            $this->locationServiceMock = $this->createMock(APILocationService::class);
6112
        }
6113
6114
        return $this->locationServiceMock;
6115
    }
6116
6117
    /** @var \eZ\Publish\Core\Repository\ContentService */
6118
    protected $partlyMockedContentService;
6119
6120
    /**
6121
     * Returns the content service to test with $methods mocked.
6122
     *
6123
     * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()}
6124
     *
6125
     * @param string[] $methods
6126
     *
6127
     * @return \eZ\Publish\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject
6128
     */
6129
    protected function getPartlyMockedContentService(array $methods = null)
6130
    {
6131
        if (!isset($this->partlyMockedContentService)) {
6132
            $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class)
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getMockBuilder(\e...), array()))->getMock() of type object<PHPUnit\Framework\MockObject\MockObject> is incompatible with the declared type object<eZ\Publish\Core\Repository\ContentService> of property $partlyMockedContentService.

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

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

Loading history...
6133
                ->setMethods($methods)
6134
                ->setConstructorArgs(
6135
                    [
6136
                        $this->getRepositoryMock(),
6137
                        $this->getPersistenceMock(),
6138
                        $this->getDomainMapperMock(),
6139
                        $this->getRelationProcessorMock(),
6140
                        $this->getNameSchemaServiceMock(),
6141
                        $this->getFieldTypeRegistryMock(),
6142
                        [],
6143
                    ]
6144
                )
6145
                ->getMock();
6146
        }
6147
6148
        return $this->partlyMockedContentService;
6149
    }
6150
6151
    /**
6152
     * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject
6153
     */
6154
    protected function getRepositoryMock(): Repository
6155
    {
6156
        $repositoryMock = parent::getRepositoryMock();
6157
        $repositoryMock
6158
            ->expects($this->any())
6159
            ->method('getPermissionResolver')
6160
            ->willReturn($this->getPermissionResolverMock());
6161
6162
        return $repositoryMock;
6163
    }
6164
}
6165