Completed
Push — 6.13 ( 29a2d0...1955db )
by
unknown
78:32 queued 41:45
created

BaseTest::getRawDatabaseConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the BaseTest class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Tests;
10
11
use Doctrine\DBAL\Connection;
12
use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException;
13
use eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ValidationErrorOccurs as PHPUnitConstraintValidationErrorOccurs;
14
use eZ\Publish\API\Repository\Values\Content\Location;
15
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
16
use PHPUnit\Framework\TestCase;
17
use eZ\Publish\API\Repository\Repository;
18
use eZ\Publish\API\Repository\Values\ValueObject;
19
use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation;
20
use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation;
21
use eZ\Publish\API\Repository\Values\User\UserGroup;
22
use eZ\Publish\Core\REST\Client\Sessionable;
23
use DateTime;
24
use ArrayObject;
25
use Exception;
26
use PDOException;
27
28
/**
29
 * Base class for api specific tests.
30
 */
31
abstract class BaseTest extends TestCase
32
{
33
    /**
34
     * Maximum integer number accepted by the different backends.
35
     */
36
    const DB_INT_MAX = 2147483647;
37
38
    /**
39
     * @var \eZ\Publish\API\Repository\Tests\SetupFactory
40
     */
41
    private $setupFactory;
42
43
    /**
44
     * @var \eZ\Publish\API\Repository\Repository
45
     */
46
    private $repository;
47
48
    protected function setUp()
49
    {
50
        parent::setUp();
51
52
        try {
53
            // Use setup factory instance here w/o clearing data in case test don't need to
54
            $repository = $this->getSetupFactory()->getRepository(false);
55
56
            // Set session if we are testing the REST backend to make it
57
            // possible to persist data in the memory backend during multiple
58
            // requests.
59
            if ($repository instanceof Sessionable) {
60
                $repository->setSession($id = md5(microtime()));
61
            }
62
        } catch (PDOException $e) {
63
            $this->fail(
64
                'The communication with the database cannot be established. ' .
65
                "This is required in order to perform the tests.\n\n" .
66
                'Exception: ' . $e
67
            );
68
        } catch (Exception $e) {
69
            $this->fail(
70
                'Cannot create a repository with predefined user. ' .
71
                'Check the UserService or RoleService implementation. ' .
72
                PHP_EOL . PHP_EOL .
73
                'Exception: ' . $e
74
            );
75
        }
76
    }
77
78
    /**
79
     * Resets the temporary used repository between each test run.
80
     */
81
    protected function tearDown()
82
    {
83
        $this->repository = null;
84
        parent::tearDown();
85
    }
86
87
    /**
88
     * Returns the ID generator, fitting to the repository implementation.
89
     *
90
     * @return \eZ\Publish\API\Repository\Tests\IdManager
91
     */
92
    protected function getIdManager()
93
    {
94
        return $this->getSetupFactory()->getIdManager();
95
    }
96
97
    /**
98
     * Generates a repository specific ID value.
99
     *
100
     * @param string $type
101
     * @param mixed $rawId
102
     *
103
     * @return mixed
104
     */
105
    protected function generateId($type, $rawId)
106
    {
107
        return $this->getIdManager()->generateId($type, $rawId);
108
    }
109
110
    /**
111
     * Parses a repository specific ID value.
112
     *
113
     * @param string $type
114
     * @param mixed $id
115
     *
116
     * @return mixed
117
     */
118
    protected function parseId($type, $id)
119
    {
120
        return $this->getIdManager()->parseId($type, $id);
121
    }
122
123
    /**
124
     * Returns a config setting provided by the setup factory.
125
     *
126
     * @param string $configKey
127
     *
128
     * @return mixed
129
     */
130
    protected function getConfigValue($configKey)
131
    {
132
        return $this->getSetupFactory()->getConfigValue($configKey);
133
    }
134
135
    /**
136
     * Tests if the currently tested api is based on a V4 implementation.
137
     *
138
     * @return bool
139
     */
140
    protected function isVersion4()
141
    {
142
        return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion'];
143
    }
144
145
    /**
146
     * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test
147
     *
148
     * @return \eZ\Publish\API\Repository\Repository
149
     */
150
    protected function getRepository($initialInitializeFromScratch = true)
151
    {
152
        if (null === $this->repository) {
153
            $this->repository = $this->getSetupFactory()->getRepository($initialInitializeFromScratch);
154
        }
155
156
        return $this->repository;
157
    }
158
159
    /**
160
     * @return \eZ\Publish\API\Repository\Tests\SetupFactory
161
     */
162
    protected function getSetupFactory()
163
    {
164
        if (null === $this->setupFactory) {
165
            if (false === isset($_ENV['setupFactory'])) {
166
                throw new \ErrorException(
167
                    'Missing mandatory setting $_ENV["setupFactory"], this should normally be set in the relevant phpunit-integration-*.xml file and refer to a setupFactory for the given StorageEngine/SearchEngine in use'
168
                );
169
            }
170
171
            $setupClass = $_ENV['setupFactory'];
172
            if (false === class_exists($setupClass)) {
173
                throw new \ErrorException(
174
                    sprintf(
175
                        '$_ENV["setupFactory"] does not reference an existing class: %s. Did you forget to install an package dependency?',
176
                        $setupClass
177
                    )
178
                );
179
            }
180
181
            $this->setupFactory = new $setupClass();
182
        }
183
184
        return $this->setupFactory;
185
    }
186
187
    /**
188
     * Asserts that properties given in $expectedValues are correctly set in
189
     * $actualObject.
190
     *
191
     * @param mixed[] $expectedValues
192
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
193
     */
194
    protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject)
195
    {
196
        foreach ($expectedValues as $propertyName => $propertyValue) {
197
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
198
        }
199
    }
200
201
    /**
202
     * Asserts that properties given in $expectedValues are correctly set in
203
     * $actualObject.
204
     *
205
     * If the property type is array, it will be sorted before comparison.
206
     *
207
     * @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734
208
     *
209
     * @param mixed[] $expectedValues
210
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
211
     */
212
    protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject)
213
    {
214
        foreach ($expectedValues as $propertyName => $propertyValue) {
215
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true);
216
        }
217
    }
218
219
    /**
220
     * Asserts all properties from $expectedValues are correctly set in
221
     * $actualObject. Additional (virtual) properties can be asserted using
222
     * $additionalProperties.
223
     *
224
     * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues
225
     * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject
226
     * @param array $propertyNames
0 ignored issues
show
Bug introduced by
There is no parameter named $propertyNames. 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...
227
     */
228
    protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = array())
229
    {
230
        foreach ($expectedValues as $propertyName => $propertyValue) {
0 ignored issues
show
Bug introduced by
The expression $expectedValues of type object<eZ\Publish\API\Re...ory\Values\ValueObject> is not traversable.
Loading history...
231
            $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName);
232
        }
233
234
        foreach ($additionalProperties as $propertyName) {
235
            $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName);
236
        }
237
    }
238
239
    /**
240
     * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted()
241
     *
242
     * @param array $items An array of scalar values
243
     */
244
    private function sortItems(array &$items)
245
    {
246
        $sorter = function ($a, $b) {
247
            if (!is_scalar($a) || !is_scalar($b)) {
248
                $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values');
249
            }
250
251
            return strcmp($a, $b);
252
        };
253
        usort($items, $sorter);
254
    }
255
256
    private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false)
257
    {
258
        if ($expectedValue instanceof ArrayObject) {
259
            $expectedValue = $expectedValue->getArrayCopy();
260
        } elseif ($expectedValue instanceof DateTime) {
261
            $expectedValue = $expectedValue->format(DateTime::RFC850);
262
        }
263
        if ($actualValue instanceof ArrayObject) {
264
            $actualValue = $actualValue->getArrayCopy();
265
        } elseif ($actualValue instanceof DateTime) {
266
            $actualValue = $actualValue->format(DateTime::RFC850);
267
        }
268
269
        if ($sortArray && is_array($actualValue) && is_array($expectedValue)) {
270
            $this->sortItems($actualValue);
271
            $this->sortItems($expectedValue);
272
        }
273
274
        $this->assertEquals(
275
            $expectedValue,
276
            $actualValue,
277
            sprintf('Object property "%s" incorrect.', $propertyName)
278
        );
279
    }
280
281
    /**
282
     * Create a user in editor user group.
283
     *
284
     * @param string $login
285
     *
286
     * @return \eZ\Publish\API\Repository\Values\User\User
287
     */
288
    protected function createUserVersion1($login = 'user')
289
    {
290
        $repository = $this->getRepository();
291
292
        /* BEGIN: Inline */
293
        // ID of the "Editors" user group in an eZ Publish demo installation
294
        $editorsGroupId = 13;
295
296
        $userService = $repository->getUserService();
297
298
        // Instantiate a create struct with mandatory properties
299
        $userCreate = $userService->newUserCreateStruct(
300
            $login,
301
            "{$login}@example.com",
302
            'secret',
303
            'eng-US'
304
        );
305
        $userCreate->enabled = true;
306
307
        // Set some fields required by the user ContentType
308
        $userCreate->setField('first_name', 'Example');
309
        $userCreate->setField('last_name', 'User');
310
311
        // Load parent group for the user
312
        $group = $userService->loadUserGroup($editorsGroupId);
313
314
        // Create a new user instance.
315
        $user = $userService->createUser($userCreate, array($group));
316
        /* END: Inline */
317
318
        return $user;
319
    }
320
321
    /**
322
     * Create a user in new user group with editor rights limited to Media Library (/1/48/).
323
     *
324
     * @uses ::createCustomUserVersion1()
325
     *
326
     * @return \eZ\Publish\API\Repository\Values\User\User
327
     */
328
    protected function createMediaUserVersion1()
329
    {
330
        return $this->createCustomUserVersion1(
331
            'Media Editor',
332
            'Editor',
333
            new SubtreeLimitation(array('limitationValues' => array('/1/43/')))
334
        );
335
    }
336
337
    /**
338
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
339
     *
340
     * @param string $userGroupName Name of the new user group to create
341
     * @param string $roleIdentifier Role identifier to assign to the new group
342
     * @param RoleLimitation|null $roleLimitation
343
     *
344
     * @return \eZ\Publish\API\Repository\Values\User\User
345
     */
346
    protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null)
347
    {
348
        return $this->createCustomUserWithLogin(
349
            'user',
350
            '[email protected]',
351
            $userGroupName,
352
            $roleIdentifier,
353
            $roleLimitation
354
        );
355
    }
356
357
    /**
358
     * Create a user with new user group and assign a existing role (optionally with RoleLimitation).
359
     *
360
     * @param string $login User login
361
     * @param string $email User e-mail
362
     * @param string $userGroupName Name of the new user group to create
363
     * @param string $roleIdentifier Role identifier to assign to the new group
364
     * @param RoleLimitation|null $roleLimitation
365
     * @return \eZ\Publish\API\Repository\Values\User\User
366
     */
367
    protected function createCustomUserWithLogin(
368
        $login,
369
        $email,
370
        $userGroupName,
371
        $roleIdentifier,
372
        RoleLimitation $roleLimitation = null
373
    ) {
374
        $repository = $this->getRepository();
375
376
        /* BEGIN: Inline */
377
        // ID of the "Users" user group in an eZ Publish demo installation
378
        $rootUsersGroupId = $this->generateId('location', 4);
379
380
        $roleService = $repository->getRoleService();
381
        $userService = $repository->getUserService();
382
383
        // Get a group create struct
384
        $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US');
385
        $userGroupCreate->setField('name', $userGroupName);
386
387
        // Create new group with media editor rights
388
        $userGroup = $userService->createUserGroup(
389
            $userGroupCreate,
390
            $userService->loadUserGroup($rootUsersGroupId)
391
        );
392
        $roleService->assignRoleToUserGroup(
393
            $roleService->loadRoleByIdentifier($roleIdentifier),
394
            $userGroup,
395
            $roleLimitation
396
        );
397
398
        // Instantiate a create struct with mandatory properties
399
        $userCreate = $userService->newUserCreateStruct(
400
            $login,
401
            $email,
402
            'secret',
403
            'eng-US'
404
        );
405
        $userCreate->enabled = true;
406
407
        // Set some fields required by the user ContentType
408
        $userCreate->setField('first_name', 'Example');
409
        $userCreate->setField('last_name', ucfirst($login));
410
411
        // Create a new user instance.
412
        $user = $userService->createUser($userCreate, array($userGroup));
413
        /* END: Inline */
414
415
        return $user;
416
    }
417
418
    /**
419
     * Create a user using given data.
420
     *
421
     * @param string $login
422
     * @param string $firstName
423
     * @param string $lastName
424
     * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default
425
     *
426
     * @return \eZ\Publish\API\Repository\Values\User\User
427
     */
428
    protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null)
429
    {
430
        $repository = $this->getRepository();
431
432
        $userService = $repository->getUserService();
433
        if (null === $userGroup) {
434
            $userGroup = $userService->loadUserGroup(13);
435
        }
436
437
        // Instantiate a create struct with mandatory properties
438
        $userCreate = $userService->newUserCreateStruct(
439
            $login,
440
            "{$login}@example.com",
441
            'secret',
442
            'eng-US'
443
        );
444
        $userCreate->enabled = true;
445
446
        // Set some fields required by the user ContentType
447
        $userCreate->setField('first_name', $firstName);
448
        $userCreate->setField('last_name', $lastName);
449
450
        // Create a new user instance.
451
        $user = $userService->createUser($userCreate, array($userGroup));
452
453
        return $user;
454
    }
455
456
    /**
457
     * Only for internal use.
458
     *
459
     * Creates a \DateTime object for $timestamp in the current time zone
460
     *
461
     * @param int $timestamp
462
     *
463
     * @return \DateTime
464
     */
465 View Code Duplication
    public function createDateTime($timestamp = null)
466
    {
467
        $dateTime = new \DateTime();
468
        if ($timestamp !== null) {
469
            $dateTime->setTimestamp($timestamp);
470
        }
471
472
        return $dateTime;
473
    }
474
475
    /**
476
     * Calls given Repository's aggregated SearchHandler::refresh().
477
     *
478
     * Currently implemented only in Solr search engine.
479
     *
480
     * @param \eZ\Publish\API\Repository\Repository $repository
481
     */
482
    protected function refreshSearch(Repository $repository)
483
    {
484
        $setupFactory = $this->getSetupFactory();
485
486
        if (!$setupFactory instanceof LegacySolrSetupFactory) {
0 ignored issues
show
Bug introduced by
The class EzSystems\EzPlatformSolr...tory\LegacySetupFactory does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
487
            return;
488
        }
489
490
        while (true) {
491
            $repositoryReflection = new \ReflectionObject($repository);
492
            // If the repository is decorated, we need to recurse in the "repository" property
493
            if (!$repositoryReflection->hasProperty('repository')) {
494
                break;
495
            }
496
497
            $repositoryProperty = $repositoryReflection->getProperty('repository');
498
            $repositoryProperty->setAccessible(true);
499
            $repository = $repositoryProperty->getValue($repository);
500
        }
501
502
        $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler');
503
        $searchHandlerProperty->setAccessible(true);
504
505
        /** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */
506
        $searchHandler = $searchHandlerProperty->getValue($repository);
507
508
        $searchHandler->commit();
509
    }
510
511
    /**
512
     * Create role of a given name with the given policies described by an array.
513
     *
514
     * @param $roleName
515
     * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []]
516
     *
517
     * @return \eZ\Publish\API\Repository\Values\User\Role
518
     *
519
     * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
520
     * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException
521
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
522
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
523
     */
524
    public function createRoleWithPolicies($roleName, array $policiesData)
525
    {
526
        $repository = $this->getRepository(false);
527
        $roleService = $repository->getRoleService();
528
529
        $roleCreateStruct = $roleService->newRoleCreateStruct($roleName);
530
        foreach ($policiesData as $policyData) {
531
            $policyCreateStruct = $roleService->newPolicyCreateStruct(
532
                $policyData['module'],
533
                $policyData['function']
534
            );
535
536
            if (isset($policyData['limitations'])) {
537
                foreach ($policyData['limitations'] as $limitation) {
538
                    $policyCreateStruct->addLimitation($limitation);
539
                }
540
            }
541
542
            $roleCreateStruct->addPolicy($policyCreateStruct);
543
        }
544
545
        $roleDraft = $roleService->createRole($roleCreateStruct);
546
547
        $roleService->publishRoleDraft($roleDraft);
548
549
        return $roleService->loadRole($roleDraft->id);
550
    }
551
552
    /**
553
     * Create user and assign new role with the given policies.
554
     *
555
     * @param string $login
556
     * @param array $policiesData list of policies in the form of <code>[ [ 'module' => 'name', 'function' => 'name'] ]</code>
557
     *
558
     * @return \eZ\Publish\API\Repository\Values\User\User
559
     *
560
     * @throws \Exception
561
     */
562
    public function createUserWithPolicies($login, array $policiesData)
563
    {
564
        $repository = $this->getRepository(false);
565
        $roleService = $repository->getRoleService();
566
        $userService = $repository->getUserService();
567
568
        $repository->beginTransaction();
569
        try {
570
            $userCreateStruct = $userService->newUserCreateStruct(
571
                $login,
572
                "{$login}@test.local",
573
                $login,
574
                'eng-GB'
575
            );
576
            $userCreateStruct->setField('first_name', $login);
577
            $userCreateStruct->setField('last_name', $login);
578
            $user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]);
579
580
            $role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_'), $policiesData);
581
            $roleService->assignRoleToUser($role, $user);
582
583
            $repository->commit();
584
585
            return $user;
586
        } catch (\Exception $ex) {
587
            $repository->rollback();
588
            throw $ex;
589
        }
590
    }
591
592
    /**
593
     * @return \Doctrine\DBAL\Connection
594
     *
595
     * @throws \ErrorException
596
     */
597
    protected function getRawDatabaseConnection()
598
    {
599
        $connection = $this
600
            ->getSetupFactory()
601
            ->getServiceContainer()->get('ezpublish.api.storage_engine.legacy.connection');
602
603
        if (!$connection instanceof Connection) {
604
            throw new \RuntimeException(
605
                sprintf('Expected %s got %s', Connection::class, get_class($connection))
606
            );
607
        }
608
609
        return $connection;
610
    }
611
612
    /**
613
     * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection).
614
     * Returns the result returned by the given callback.
615
     *
616
     * **Note**: The method clears the entire persistence cache pool.
617
     *
618
     * @throws \Exception
619
     *
620
     * @param callable $callback
621
     *
622
     * @return mixed the return result of the given callback
623
     */
624
    public function performRawDatabaseOperation(callable $callback)
625
    {
626
        $repository = $this->getRepository(false);
627
        $repository->beginTransaction();
628
        try {
629
            $callback(
630
                $this->getRawDatabaseConnection()
631
            );
632
            $repository->commit();
633
        } catch (Exception $e) {
634
            $repository->rollback();
635
            throw $e;
636
        }
637
638
        /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */
639
        $cachePool = $this
640
            ->getSetupFactory()
641
            ->getServiceContainer()->get('ezpublish.cache_pool');
642
643
        $cachePool->clear();
644
    }
645
646
    /**
647
     * Traverse all errors for all fields in all Translations to find expected one.
648
     *
649
     * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception
650
     * @param string $expectedValidationErrorMessage
651
     */
652
    protected function assertValidationErrorOccurs(
653
        ContentFieldValidationException $exception,
654
        $expectedValidationErrorMessage
655
    ) {
656
        $constraint = new PHPUnitConstraintValidationErrorOccurs($expectedValidationErrorMessage);
657
658
        self::assertThat($exception, $constraint);
659
    }
660
661
    /**
662
     * Create 'folder' Content.
663
     *
664
     * @param array $names Folder names in the form of <code>['&lt;language_code&gt;' => '&lt;name&gt;']</code>
665
     * @param int $parentLocationId
666
     *
667
     * @return \eZ\Publish\API\Repository\Values\Content\Content published Content
668
     *
669
     * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException
670
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
671
     * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
672
     */
673
    protected function createFolder(array $names, $parentLocationId)
674
    {
675
        $repository = $this->getRepository(false);
676
        $contentService = $repository->getContentService();
677
        $contentTypeService = $repository->getContentTypeService();
678
        $locationService = $repository->getLocationService();
679
680
        if (empty($names)) {
681
            throw new \RuntimeException(sprintf('%s expects non-empty names list', __METHOD__));
682
        }
683
        $mainLanguageCode = array_keys($names)[0];
684
685
        $struct = $contentService->newContentCreateStruct(
686
            $contentTypeService->loadContentTypeByIdentifier('folder'),
687
            $mainLanguageCode
688
        );
689
        foreach ($names as $languageCode => $translatedName) {
690
            $struct->setField('name', $translatedName, $languageCode);
691
        }
692
        $contentDraft = $contentService->createContent(
693
            $struct,
694
            [$locationService->newLocationCreateStruct($parentLocationId)]
695
        );
696
697
        return $contentService->publishVersion($contentDraft->versionInfo);
698
    }
699
}
700