Exponential 5.x Kernel 7.5.40 Exponential New Stack (Symfony-based) Kernel — PHP Class Reference

Core/Repository/ContentTypeService.php
Go to the documentation of this file.
1 <?php
2
7 namespace eZ\Publish\Core\Repository;
8
9 use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface;
10 use eZ\Publish\API\Repository\Repository as RepositoryInterface;
11 use eZ\Publish\API\Repository\Values\ContentType\ContentType;
12 use eZ\Publish\SPI\Persistence\Content\Type\Handler;
13 use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
14 use eZ\Publish\API\Repository\Exceptions\BadStateException as APIBadStateException;
15 use eZ\Publish\API\Repository\Values\User\User;
16 use eZ\Publish\SPI\Persistence\User\Handler as UserHandler;
17 use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct;
18 use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition;
19 use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct;
20 use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType;
21 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft as APIContentTypeDraft;
22 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup as APIContentTypeGroup;
23 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct;
24 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct as APIContentTypeCreateStruct;
25 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct;
26 use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct;
27 use eZ\Publish\API\Repository\Values\Content\Location;
28 use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeCreateStruct;
29 use eZ\Publish\SPI\Persistence\Content\Type as SPIContentType;
30 use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct as SPIContentTypeCreateStruct;
31 use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as SPIContentTypeGroupCreateStruct;
32 use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as SPIContentTypeGroupUpdateStruct;
33 use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType;
34 use eZ\Publish\Core\Base\Exceptions\NotFoundException;
35 use eZ\Publish\Core\Base\Exceptions\BadStateException;
36 use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue;
37 use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType;
38 use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
39 use eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException;
40 use eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException;
41 use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
42 use eZ\Publish\Core\FieldType\ValidationError;
43 use DateTime;
44 use Exception;
45
46 class ContentTypeService implements ContentTypeServiceInterface
47 {
49 protected $repository;
50
52 protected $contentTypeHandler;
53
55 protected $userHandler;
56
58 protected $settings;
59
61 protected $domainMapper;
62
64 protected $contentTypeDomainMapper;
65
67 protected $fieldTypeRegistry;
68
80 public function __construct(
81 RepositoryInterface $repository,
82 Handler $contentTypeHandler,
83 UserHandler $userHandler,
84 Helper\DomainMapper $domainMapper,
85 Helper\ContentTypeDomainMapper $contentTypeDomainMapper,
86 Helper\FieldTypeRegistry $fieldTypeRegistry,
87 array $settings = []
88 ) {
89 $this->repository = $repository;
90 $this->contentTypeHandler = $contentTypeHandler;
91 $this->userHandler = $userHandler;
92 $this->domainMapper = $domainMapper;
93 $this->contentTypeDomainMapper = $contentTypeDomainMapper;
94 $this->fieldTypeRegistry = $fieldTypeRegistry;
95 // Union makes sure default settings are ignored if provided in argument
96 $this->settings = $settings + [
97 //'defaultSetting' => array(),
98 ];
99 }
100
111 public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct)
112 {
113 if (!$this->repository->canUser('class', 'create', $contentTypeGroupCreateStruct)) {
114 throw new UnauthorizedException('ContentType', 'create');
115 }
116
117 try {
118 $this->loadContentTypeGroupByIdentifier($contentTypeGroupCreateStruct->identifier);
119
120 throw new InvalidArgumentException(
121 '$contentTypeGroupCreateStruct',
122 "A group with the identifier '{$contentTypeGroupCreateStruct->identifier}' already exists"
123 );
124 } catch (APINotFoundException $e) {
125 // Do nothing
126 }
127
128 if ($contentTypeGroupCreateStruct->creationDate === null) {
129 $timestamp = time();
130 } else {
131 $timestamp = $contentTypeGroupCreateStruct->creationDate->getTimestamp();
132 }
133
134 if ($contentTypeGroupCreateStruct->creatorId === null) {
135 $userId = $this->repository->getCurrentUserReference()->getUserId();
136 } else {
137 $userId = $contentTypeGroupCreateStruct->creatorId;
138 }
139
140 $spiGroupCreateStruct = new SPIContentTypeGroupCreateStruct(
141 [
142 'identifier' => $contentTypeGroupCreateStruct->identifier,
143 'created' => $timestamp,
144 'modified' => $timestamp,
145 'creatorId' => $userId,
146 'modifierId' => $userId,
147 ]
148 );
149
150 $this->repository->beginTransaction();
151 try {
152 $spiContentTypeGroup = $this->contentTypeHandler->createGroup(
153 $spiGroupCreateStruct
154 );
155 $this->repository->commit();
156 } catch (Exception $e) {
157 $this->repository->rollback();
158 throw $e;
159 }
160
161 return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiContentTypeGroup);
162 }
163
167 public function loadContentTypeGroup($contentTypeGroupId, array $prioritizedLanguages = [])
168 {
169 $spiGroup = $this->contentTypeHandler->loadGroup(
170 $contentTypeGroupId
171 );
172
173 return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages);
174 }
175
179 public function loadContentTypeGroupByIdentifier($contentTypeGroupIdentifier, array $prioritizedLanguages = [])
180 {
181 $groups = $this->loadContentTypeGroups($prioritizedLanguages);
182
183 foreach ($groups as $group) {
184 if ($group->identifier === $contentTypeGroupIdentifier) {
185 return $group;
186 }
187 }
188
189 throw new NotFoundException('ContentTypeGroup', $contentTypeGroupIdentifier);
190 }
191
195 public function loadContentTypeGroups(array $prioritizedLanguages = [])
196 {
197 $spiGroups = $this->contentTypeHandler->loadAllGroups();
198
199 $groups = [];
200 foreach ($spiGroups as $spiGroup) {
201 $groups[] = $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages);
202 }
203
204 return $groups;
205 }
206
216 public function updateContentTypeGroup(APIContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct)
217 {
218 if (!$this->repository->canUser('class', 'update', $contentTypeGroup)) {
219 throw new UnauthorizedException('ContentType', 'update');
220 }
221
222 $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id);
223
224 if ($contentTypeGroupUpdateStruct->identifier !== null
225 && $contentTypeGroupUpdateStruct->identifier !== $loadedContentTypeGroup->identifier) {
226 try {
227 $this->loadContentTypeGroupByIdentifier($contentTypeGroupUpdateStruct->identifier);
228
229 throw new InvalidArgumentException(
230 '$contentTypeGroupUpdateStruct->identifier',
231 'given identifier already exists'
232 );
233 } catch (APINotFoundException $e) {
234 // Do nothing
235 }
236 }
237
238 if ($contentTypeGroupUpdateStruct->modificationDate !== null) {
239 $modifiedTimestamp = $contentTypeGroupUpdateStruct->modificationDate->getTimestamp();
240 } else {
241 $modifiedTimestamp = time();
242 }
243
244 $spiGroupUpdateStruct = new SPIContentTypeGroupUpdateStruct(
245 [
246 'id' => $loadedContentTypeGroup->id,
247 'identifier' => $contentTypeGroupUpdateStruct->identifier === null ?
248 $loadedContentTypeGroup->identifier :
249 $contentTypeGroupUpdateStruct->identifier,
250 'modified' => $modifiedTimestamp,
251 'modifierId' => $contentTypeGroupUpdateStruct->modifierId === null ?
252 $this->repository->getCurrentUserReference()->getUserId() :
253 $contentTypeGroupUpdateStruct->modifierId,
254 ]
255 );
256
257 $this->repository->beginTransaction();
258 try {
259 $this->contentTypeHandler->updateGroup(
260 $spiGroupUpdateStruct
261 );
262 $this->repository->commit();
263 } catch (Exception $e) {
264 $this->repository->rollback();
265 throw $e;
266 }
267 }
268
279 public function deleteContentTypeGroup(APIContentTypeGroup $contentTypeGroup)
280 {
281 if (!$this->repository->canUser('class', 'delete', $contentTypeGroup)) {
282 throw new UnauthorizedException('ContentType', 'delete');
283 }
284
285 $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id);
286
287 $this->repository->beginTransaction();
288 try {
289 $this->contentTypeHandler->deleteGroup(
290 $loadedContentTypeGroup->id
291 );
292 $this->repository->commit();
293 } catch (APIBadStateException $e) {
294 $this->repository->rollback();
295 throw new InvalidArgumentException(
296 '$contentTypeGroup',
297 'Content type group has content type instances',
298 $e
299 );
300 } catch (Exception $e) {
301 $this->repository->rollback();
302 throw $e;
303 }
304 }
305
315 protected function validateInputContentTypeCreateStruct(APIContentTypeCreateStruct $contentTypeCreateStruct)
316 {
317 // Required properties
318
319 if ($contentTypeCreateStruct->identifier === null) {
320 throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'identifier' is required");
321 }
322
323 if (!is_string($contentTypeCreateStruct->identifier)) {
324 throw new InvalidArgumentType(
325 '$contentTypeCreateStruct->identifier',
326 'string',
327 $contentTypeCreateStruct->identifier
328 );
329 }
330
331 if ($contentTypeCreateStruct->identifier === '') {
332 throw new InvalidArgumentValue(
333 '$contentTypeCreateStruct->identifier',
334 $contentTypeCreateStruct->identifier
335 );
336 }
337
338 if ($contentTypeCreateStruct->mainLanguageCode === null) {
339 throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'mainLanguageCode' is required");
340 }
341
342 if (!is_string($contentTypeCreateStruct->mainLanguageCode)) {
343 throw new InvalidArgumentType(
344 '$contentTypeCreateStruct->mainLanguageCode',
345 'string',
346 $contentTypeCreateStruct->mainLanguageCode
347 );
348 }
349
350 if ($contentTypeCreateStruct->mainLanguageCode === '') {
351 throw new InvalidArgumentValue(
352 '$contentTypeCreateStruct->mainLanguageCode',
353 $contentTypeCreateStruct->mainLanguageCode
354 );
355 }
356
357 if ($contentTypeCreateStruct->names !== null) {
358 $this->domainMapper->validateTranslatedList(
359 $contentTypeCreateStruct->names,
360 '$contentTypeCreateStruct->names'
361 );
362 }
363
364 if (!isset($contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode]) ||
365 $contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode] === ''
366 ) {
367 throw new InvalidArgumentException(
368 '$contentTypeCreateStruct->names',
369 'At least one name in the main language is required'
370 );
371 }
372
373 // Optional properties
374
375 if ($contentTypeCreateStruct->descriptions !== null) {
376 $this->domainMapper->validateTranslatedList(
377 $contentTypeCreateStruct->descriptions,
378 '$contentTypeCreateStruct->descriptions'
379 );
380 }
381
382 if ($contentTypeCreateStruct->defaultSortField !== null && !$this->domainMapper->isValidLocationSortField($contentTypeCreateStruct->defaultSortField)) {
383 throw new InvalidArgumentValue(
384 '$contentTypeCreateStruct->defaultSortField',
385 $contentTypeCreateStruct->defaultSortField
386 );
387 }
388
389 if ($contentTypeCreateStruct->defaultSortOrder !== null && !$this->domainMapper->isValidLocationSortOrder($contentTypeCreateStruct->defaultSortOrder)) {
390 throw new InvalidArgumentValue(
391 '$contentTypeCreateStruct->defaultSortOrder',
392 $contentTypeCreateStruct->defaultSortOrder
393 );
394 }
395
396 if ($contentTypeCreateStruct->creatorId !== null) {
397 $this->repository->getUserService()->loadUser($contentTypeCreateStruct->creatorId);
398 }
399
400 if ($contentTypeCreateStruct->creationDate !== null && !$contentTypeCreateStruct->creationDate instanceof DateTime) {
401 throw new InvalidArgumentType(
402 '$contentTypeCreateStruct->creationDate',
403 'DateTime',
404 $contentTypeCreateStruct->creationDate
405 );
406 }
407
408 if ($contentTypeCreateStruct->defaultAlwaysAvailable !== null && !is_bool($contentTypeCreateStruct->defaultAlwaysAvailable)) {
409 throw new InvalidArgumentType(
410 '$contentTypeCreateStruct->defaultAlwaysAvailable',
411 'boolean',
412 $contentTypeCreateStruct->defaultAlwaysAvailable
413 );
414 }
415
416 if ($contentTypeCreateStruct->isContainer !== null && !is_bool($contentTypeCreateStruct->isContainer)) {
417 throw new InvalidArgumentType(
418 '$contentTypeCreateStruct->isContainer',
419 'boolean',
420 $contentTypeCreateStruct->isContainer
421 );
422 }
423
424 if ($contentTypeCreateStruct->remoteId !== null && !is_string($contentTypeCreateStruct->remoteId)) {
425 throw new InvalidArgumentType(
426 '$contentTypeCreateStruct->remoteId',
427 'string',
428 $contentTypeCreateStruct->remoteId
429 );
430 }
431
432 if ($contentTypeCreateStruct->nameSchema !== null && !is_string($contentTypeCreateStruct->nameSchema)) {
433 throw new InvalidArgumentType(
434 '$contentTypeCreateStruct->nameSchema',
435 'string',
436 $contentTypeCreateStruct->nameSchema
437 );
438 }
439
440 if ($contentTypeCreateStruct->urlAliasSchema !== null && !is_string($contentTypeCreateStruct->urlAliasSchema)) {
441 throw new InvalidArgumentType(
442 '$contentTypeCreateStruct->urlAliasSchema',
443 'string',
444 $contentTypeCreateStruct->urlAliasSchema
445 );
446 }
447
448 foreach ($contentTypeCreateStruct->fieldDefinitions as $key => $fieldDefinitionCreateStruct) {
449 if (!$fieldDefinitionCreateStruct instanceof FieldDefinitionCreateStruct) {
450 throw new InvalidArgumentType(
451 "\$contentTypeCreateStruct->fieldDefinitions[$key]",
452 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinitionCreateStruct',
453 $fieldDefinitionCreateStruct
454 );
455 }
456
457 $this->validateInputFieldDefinitionCreateStruct(
458 $fieldDefinitionCreateStruct,
459 "\$contentTypeCreateStruct->fieldDefinitions[$key]"
460 );
461 }
462 }
463
472 protected function validateInputContentTypeGroups(array $contentTypeGroups)
473 {
474 if (empty($contentTypeGroups)) {
475 throw new InvalidArgumentException(
476 '$contentTypeGroups',
477 'Argument must contain at least one ContentTypeGroup'
478 );
479 }
480
481 foreach ($contentTypeGroups as $key => $contentTypeGroup) {
482 if (!$contentTypeGroup instanceof APIContentTypeGroup) {
483 throw new InvalidArgumentType(
484 "\$contentTypeGroups[{$key}]",
485 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroup',
486 $contentTypeGroup
487 );
488 }
489 }
490 }
491
502 protected function validateInputFieldDefinitionCreateStruct(
503 FieldDefinitionCreateStruct $fieldDefinitionCreateStruct,
504 $argumentName = '$fieldDefinitionCreateStruct'
505 ) {
506 // Required properties
507
508 if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === null) {
509 throw new InvalidArgumentException($argumentName, "Property 'fieldTypeIdentifier' is required");
510 }
511
512 if (!is_string($fieldDefinitionCreateStruct->fieldTypeIdentifier)) {
513 throw new InvalidArgumentType(
514 $argumentName . '->fieldTypeIdentifier',
515 'string',
516 $fieldDefinitionCreateStruct->fieldTypeIdentifier
517 );
518 }
519
520 if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === '') {
521 throw new InvalidArgumentValue(
522 $argumentName . '->fieldTypeIdentifier',
523 $fieldDefinitionCreateStruct->fieldTypeIdentifier
524 );
525 }
526
527 if ($fieldDefinitionCreateStruct->identifier === null) {
528 throw new InvalidArgumentException($argumentName, "Property 'identifier' is required");
529 }
530
531 if (!is_string($fieldDefinitionCreateStruct->identifier)) {
532 throw new InvalidArgumentType(
533 $argumentName . '->identifier',
534 'string',
535 $fieldDefinitionCreateStruct->identifier
536 );
537 }
538
539 if ($fieldDefinitionCreateStruct->identifier === '') {
540 throw new InvalidArgumentValue(
541 $argumentName . '->identifier',
542 $fieldDefinitionCreateStruct->identifier
543 );
544 }
545
546 // Optional properties
547
548 if ($fieldDefinitionCreateStruct->names !== null) {
549 $this->domainMapper->validateTranslatedList(
550 $fieldDefinitionCreateStruct->names,
551 $argumentName . '->names'
552 );
553 }
554
555 if ($fieldDefinitionCreateStruct->descriptions !== null) {
556 $this->domainMapper->validateTranslatedList(
557 $fieldDefinitionCreateStruct->descriptions,
558 $argumentName . '->descriptions'
559 );
560 }
561
562 if ($fieldDefinitionCreateStruct->fieldGroup !== null && !is_string($fieldDefinitionCreateStruct->fieldGroup)) {
563 throw new InvalidArgumentType(
564 $argumentName . '->fieldGroup',
565 'string',
566 $fieldDefinitionCreateStruct->fieldGroup
567 );
568 }
569
570 if ($fieldDefinitionCreateStruct->position !== null && !is_int($fieldDefinitionCreateStruct->position)) {
571 throw new InvalidArgumentType(
572 $argumentName . '->position',
573 'integer',
574 $fieldDefinitionCreateStruct->position
575 );
576 }
577
578 if ($fieldDefinitionCreateStruct->isTranslatable !== null && !is_bool($fieldDefinitionCreateStruct->isTranslatable)) {
579 throw new InvalidArgumentType(
580 $argumentName . '->isTranslatable',
581 'boolean',
582 $fieldDefinitionCreateStruct->isTranslatable
583 );
584 }
585
586 if ($fieldDefinitionCreateStruct->isRequired !== null && !is_bool($fieldDefinitionCreateStruct->isRequired)) {
587 throw new InvalidArgumentType(
588 $argumentName . '->isRequired',
589 'boolean',
590 $fieldDefinitionCreateStruct->isRequired
591 );
592 }
593
594 if ($fieldDefinitionCreateStruct->isInfoCollector !== null && !is_bool($fieldDefinitionCreateStruct->isInfoCollector)) {
595 throw new InvalidArgumentType(
596 $argumentName . '->isInfoCollector',
597 'boolean',
598 $fieldDefinitionCreateStruct->isInfoCollector
599 );
600 }
601
602 if ($fieldDefinitionCreateStruct->isSearchable !== null && !is_bool($fieldDefinitionCreateStruct->isSearchable)) {
603 throw new InvalidArgumentType(
604 $argumentName . '->isSearchable',
605 'boolean',
606 $fieldDefinitionCreateStruct->isSearchable
607 );
608 }
609
610 // These properties are of type 'mixed' and are validated separately by the corresponding field type
611 // validatorConfiguration
612 // fieldSettings
613 // defaultValue
614 }
615
636 public function createContentType(APIContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups)
637 {
638 if (!$this->repository->canUser('class', 'create', $contentTypeCreateStruct, $contentTypeGroups)) {
639 throw new UnauthorizedException('ContentType', 'create');
640 }
641
642 // Prevent argument mutation
643 $contentTypeCreateStruct = clone $contentTypeCreateStruct;
644 $this->validateInputContentTypeCreateStruct($contentTypeCreateStruct);
645 $this->validateInputContentTypeGroups($contentTypeGroups);
646 $initialLanguageId = $this->repository->getContentLanguageService()->loadLanguage(
647 $contentTypeCreateStruct->mainLanguageCode
648 )->id;
649
650 try {
651 $this->contentTypeHandler->loadByIdentifier(
652 $contentTypeCreateStruct->identifier
653 );
654
655 throw new InvalidArgumentException(
656 '$contentTypeCreateStruct',
657 "Another ContentType with identifier '{$contentTypeCreateStruct->identifier}' exists"
658 );
659 } catch (APINotFoundException $e) {
660 // Do nothing
661 }
662
663 if ($contentTypeCreateStruct->remoteId !== null) {
664 try {
665 $this->contentTypeHandler->loadByRemoteId(
666 $contentTypeCreateStruct->remoteId
667 );
668
669 throw new InvalidArgumentException(
670 '$contentTypeCreateStruct',
671 "Another ContentType with remoteId '{$contentTypeCreateStruct->remoteId}' exists"
672 );
673 } catch (APINotFoundException $e) {
674 // Do nothing
675 }
676 }
677
678 $fieldDefinitionIdentifierSet = [];
679 $fieldDefinitionPositionSet = [];
680 foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) {
681 // Check for duplicate identifiers
682 if (!isset($fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier])) {
683 $fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier] = true;
684 } else {
685 throw new InvalidArgumentException(
686 '$contentTypeCreateStruct',
687 "Argument contains duplicate field definition identifier '{$fieldDefinitionCreateStruct->identifier}'"
688 );
689 }
690
691 // Check for duplicate positions
692 if (!isset($fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position])) {
693 $fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position] = true;
694 } else {
695 throw new InvalidArgumentException(
696 '$contentTypeCreateStruct',
697 "Argument contains duplicate field definition position '{$fieldDefinitionCreateStruct->position}'"
698 );
699 }
700 }
701
702 $allValidationErrors = [];
703 $spiFieldDefinitions = [];
704 $fieldTypeIdentifierSet = [];
705 foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) {
707 $fieldType = $this->fieldTypeRegistry->getFieldType(
708 $fieldDefinitionCreateStruct->fieldTypeIdentifier
709 );
710
711 if ($fieldType->isSingular() && isset($fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier])) {
712 throw new ContentTypeValidationException(
713 "FieldType '%identifier%' is singular and can't be repeated in a ContentType",
714 ['%identifier%' => $fieldDefinitionCreateStruct->fieldTypeIdentifier]
715 );
716 }
717
718 $fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier] = true;
719
720 $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings);
721 $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration);
722 $validationErrors = $this->validateFieldDefinitionCreateStruct(
723 $fieldDefinitionCreateStruct,
724 $fieldType
725 );
726
727 if (!empty($validationErrors)) {
728 $allValidationErrors[$fieldDefinitionCreateStruct->identifier] = $validationErrors;
729 }
730
731 if (!empty($allValidationErrors)) {
732 continue;
733 }
734
735 $spiFieldDefinitions[] = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct(
736 $fieldDefinitionCreateStruct,
737 $fieldType,
738 $contentTypeCreateStruct->mainLanguageCode
739 );
740 }
741
742 if (!empty($allValidationErrors)) {
743 throw new ContentTypeFieldDefinitionValidationException($allValidationErrors);
744 }
745
746 $groupIds = array_map(
747 function (APIContentTypeGroup $contentTypeGroup) {
748 return $contentTypeGroup->id;
749 },
750 $contentTypeGroups
751 );
752
753 if ($contentTypeCreateStruct->creatorId === null) {
754 $contentTypeCreateStruct->creatorId = $this->repository->getCurrentUserReference()->getUserId();
755 }
756
757 if ($contentTypeCreateStruct->creationDate === null) {
758 $timestamp = time();
759 } else {
760 $timestamp = $contentTypeCreateStruct->creationDate->getTimestamp();
761 }
762
763 if ($contentTypeCreateStruct->remoteId === null) {
764 $contentTypeCreateStruct->remoteId = $this->domainMapper->getUniqueHash($contentTypeCreateStruct);
765 }
766
767 $spiContentTypeCreateStruct = new SPIContentTypeCreateStruct(
768 [
769 'identifier' => $contentTypeCreateStruct->identifier,
770 'name' => $contentTypeCreateStruct->names,
771 'status' => APIContentType::STATUS_DRAFT,
772 'description' => $contentTypeCreateStruct->descriptions === null ?
773 [] :
774 $contentTypeCreateStruct->descriptions,
775 'created' => $timestamp,
776 'modified' => $timestamp,
777 'creatorId' => $contentTypeCreateStruct->creatorId,
778 'modifierId' => $contentTypeCreateStruct->creatorId,
779 'remoteId' => $contentTypeCreateStruct->remoteId,
780 'urlAliasSchema' => $contentTypeCreateStruct->urlAliasSchema === null ?
781 '' :
782 $contentTypeCreateStruct->urlAliasSchema,
783 'nameSchema' => $contentTypeCreateStruct->nameSchema === null ?
784 '' :
785 $contentTypeCreateStruct->nameSchema,
786 'isContainer' => $contentTypeCreateStruct->isContainer === null ?
787 false :
788 $contentTypeCreateStruct->isContainer,
789 'initialLanguageId' => $initialLanguageId,
790 'sortField' => $contentTypeCreateStruct->defaultSortField === null ?
791 Location::SORT_FIELD_PUBLISHED :
792 $contentTypeCreateStruct->defaultSortField,
793 'sortOrder' => $contentTypeCreateStruct->defaultSortOrder === null ?
794 Location::SORT_ORDER_DESC :
795 $contentTypeCreateStruct->defaultSortOrder,
796 'groupIds' => $groupIds,
797 'fieldDefinitions' => $spiFieldDefinitions,
798 'defaultAlwaysAvailable' => $contentTypeCreateStruct->defaultAlwaysAvailable,
799 ]
800 );
801
802 $this->repository->beginTransaction();
803 try {
804 $spiContentType = $this->contentTypeHandler->create(
805 $spiContentTypeCreateStruct
806 );
807 $this->repository->commit();
808 } catch (Exception $e) {
809 $this->repository->rollback();
810 throw $e;
811 }
812
813 return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType);
814 }
815
824 protected function validateFieldDefinitionCreateStruct(FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, SPIFieldType $fieldType)
825 {
826 $validationErrors = [];
827
828 if ($fieldDefinitionCreateStruct->isSearchable && !$fieldType->isSearchable()) {
829 $validationErrors[] = new ValidationError(
830 "FieldType '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' is not searchable"
831 );
832 }
833
834 return array_merge(
835 $validationErrors,
836 $fieldType->validateValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration),
837 $fieldType->validateFieldSettings($fieldDefinitionCreateStruct->fieldSettings)
838 );
839 }
840
844 public function loadContentType($contentTypeId, array $prioritizedLanguages = [])
845 {
846 $spiContentType = $this->contentTypeHandler->load(
847 $contentTypeId,
848 SPIContentType::STATUS_DEFINED
849 );
850
851 return $this->contentTypeDomainMapper->buildContentTypeDomainObject(
852 $spiContentType,
853 $prioritizedLanguages
854 );
855 }
856
860 public function loadContentTypeByIdentifier($identifier, array $prioritizedLanguages = [])
861 {
862 if (!is_string($identifier)) {
863 throw new InvalidArgumentValue('$identifier', $identifier);
864 }
865
866 $spiContentType = $this->contentTypeHandler->loadByIdentifier(
867 $identifier
868 );
869
870 return $this->contentTypeDomainMapper->buildContentTypeDomainObject(
871 $spiContentType,
872 $prioritizedLanguages
873 );
874 }
875
879 public function loadContentTypeByRemoteId($remoteId, array $prioritizedLanguages = [])
880 {
881 $spiContentType = $this->contentTypeHandler->loadByRemoteId($remoteId);
882
883 return $this->contentTypeDomainMapper->buildContentTypeDomainObject(
884 $spiContentType,
885 $prioritizedLanguages
886 );
887 }
888
901 public function loadContentTypeDraft($contentTypeId, bool $ignoreOwnership = false)
902 {
903 $spiContentType = $this->contentTypeHandler->load(
904 $contentTypeId,
905 SPIContentType::STATUS_DRAFT
906 );
907
908 if (!$ignoreOwnership && $spiContentType->modifierId != $this->repository->getCurrentUserReference()->getUserId()) {
909 throw new NotFoundException('ContentType owned by someone else', $contentTypeId);
910 }
911
912 return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType);
913 }
914
918 public function loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages = []): iterable
919 {
920 $spiContentTypes = $this->contentTypeHandler->loadContentTypeList($contentTypeIds);
921 $contentTypes = [];
922
923 // @todo We could bulk load content type group proxies involved in the future & pass those relevant per type to mapper
924 foreach ($spiContentTypes as $spiContentType) {
925 $contentTypes[$spiContentType->id] = $this->contentTypeDomainMapper->buildContentTypeDomainObject(
926 $spiContentType,
927 $prioritizedLanguages
928 );
929 }
930
931 return $contentTypes;
932 }
933
937 public function loadContentTypes(APIContentTypeGroup $contentTypeGroup, array $prioritizedLanguages = [])
938 {
939 $spiContentTypes = $this->contentTypeHandler->loadContentTypes(
940 $contentTypeGroup->id,
941 SPIContentType::STATUS_DEFINED
942 );
943 $contentTypes = [];
944
945 foreach ($spiContentTypes as $spiContentType) {
946 $contentTypes[] = $this->contentTypeDomainMapper->buildContentTypeDomainObject(
947 $spiContentType,
948 $prioritizedLanguages
949 );
950 }
951
952 return $contentTypes;
953 }
954
968 public function createContentTypeDraft(APIContentType $contentType)
969 {
970 if (!$this->repository->canUser('class', 'create', $contentType)) {
971 throw new UnauthorizedException('ContentType', 'create');
972 }
973
974 try {
975 $this->contentTypeHandler->load(
976 $contentType->id,
977 SPIContentType::STATUS_DRAFT
978 );
979
980 throw new BadStateException(
981 '$contentType',
982 'Draft of the ContentType already exists'
983 );
984 } catch (APINotFoundException $e) {
985 $this->repository->beginTransaction();
986 try {
987 $spiContentType = $this->contentTypeHandler->createDraft(
988 $this->repository->getCurrentUserReference()->getUserId(),
989 $contentType->id
990 );
991 $this->repository->commit();
992 } catch (Exception $e) {
993 $this->repository->rollback();
994 throw $e;
995 }
996 }
997
998 return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType);
999 }
1000
1013 public function updateContentTypeDraft(APIContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct)
1014 {
1015 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1016 throw new UnauthorizedException('ContentType', 'update');
1017 }
1018
1019 try {
1020 $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id);
1021 } catch (APINotFoundException $e) {
1022 throw new InvalidArgumentException(
1023 '$contentTypeDraft',
1024 'There is no ContentType draft assigned to the authenticated user',
1025 $e
1026 );
1027 }
1028
1029 if ($contentTypeUpdateStruct->identifier !== null
1030 && $contentTypeUpdateStruct->identifier != $loadedContentTypeDraft->identifier) {
1031 try {
1032 $this->loadContentTypeByIdentifier($contentTypeUpdateStruct->identifier);
1033
1034 throw new InvalidArgumentException(
1035 '$contentTypeUpdateStruct',
1036 "Another ContentType with identifier '{$contentTypeUpdateStruct->identifier}' exists"
1037 );
1038 } catch (APINotFoundException $e) {
1039 // Do nothing
1040 }
1041 }
1042
1043 if ($contentTypeUpdateStruct->remoteId !== null
1044 && $contentTypeUpdateStruct->remoteId != $loadedContentTypeDraft->remoteId) {
1045 try {
1046 $this->loadContentTypeByRemoteId($contentTypeUpdateStruct->remoteId);
1047
1048 throw new InvalidArgumentException(
1049 '$contentTypeUpdateStruct',
1050 "Another ContentType with remoteId '{$contentTypeUpdateStruct->remoteId}' exists"
1051 );
1052 } catch (APINotFoundException $e) {
1053 // Do nothing
1054 }
1055 }
1056
1057 //Merge new translations into existing before update
1058 $contentTypeUpdateStruct->names = array_merge($contentTypeDraft->getNames(), $contentTypeUpdateStruct->names ?? []);
1059 $contentTypeUpdateStruct->descriptions = array_merge($contentTypeDraft->getDescriptions(), $contentTypeUpdateStruct->descriptions ?? []);
1060
1061 $this->repository->beginTransaction();
1062 try {
1063 $this->contentTypeHandler->update(
1064 $contentTypeDraft->id,
1065 $contentTypeDraft->status,
1066 $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct(
1067 $loadedContentTypeDraft,
1068 $contentTypeUpdateStruct,
1069 $this->repository->getCurrentUserReference()
1070 )
1071 );
1072 $this->repository->commit();
1073 } catch (Exception $e) {
1074 $this->repository->rollback();
1075 throw $e;
1076 }
1077 }
1078
1091 public function deleteContentType(APIContentType $contentType)
1092 {
1093 if (!$this->repository->canUser('class', 'delete', $contentType)) {
1094 throw new UnauthorizedException('ContentType', 'delete');
1095 }
1096
1097 $this->repository->beginTransaction();
1098 try {
1099 if (!$contentType instanceof APIContentTypeDraft) {
1100 $this->contentTypeHandler->delete(
1101 $contentType->id,
1102 APIContentTypeDraft::STATUS_DEFINED
1103 );
1104 }
1105
1106 $this->contentTypeHandler->delete(
1107 $contentType->id,
1108 APIContentTypeDraft::STATUS_DRAFT
1109 );
1110
1111 $this->repository->commit();
1112 } catch (Exception $e) {
1113 $this->repository->rollback();
1114 throw $e;
1115 }
1116 }
1117
1131 public function copyContentType(APIContentType $contentType, User $creator = null)
1132 {
1133 if (!$this->repository->canUser('class', 'create', $contentType)) {
1134 throw new UnauthorizedException('ContentType', 'create');
1135 }
1136
1137 if (empty($creator)) {
1138 $creator = $this->repository->getCurrentUserReference();
1139 }
1140
1141 $this->repository->beginTransaction();
1142 try {
1143 $spiContentType = $this->contentTypeHandler->copy(
1144 $creator->getUserId(),
1145 $contentType->id,
1146 SPIContentType::STATUS_DEFINED
1147 );
1148 $this->repository->commit();
1149 } catch (Exception $e) {
1150 $this->repository->rollback();
1151 throw $e;
1152 }
1153
1154 return $this->loadContentType($spiContentType->id);
1155 }
1156
1166 public function assignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup)
1167 {
1168 if (!$this->repository->canUser('class', 'update', $contentType)) {
1169 throw new UnauthorizedException('ContentType', 'update');
1170 }
1171
1172 $spiContentType = $this->contentTypeHandler->load(
1173 $contentType->id,
1174 $contentType->status
1175 );
1176
1177 if (in_array($contentTypeGroup->id, $spiContentType->groupIds)) {
1178 throw new InvalidArgumentException(
1179 '$contentTypeGroup',
1180 'The given ContentType is already assigned to the ContentTypeGroup'
1181 );
1182 }
1183
1184 $this->repository->beginTransaction();
1185 try {
1186 $this->contentTypeHandler->link(
1187 $contentTypeGroup->id,
1188 $contentType->id,
1189 $contentType->status
1190 );
1191 $this->repository->commit();
1192 } catch (Exception $e) {
1193 $this->repository->rollback();
1194 throw $e;
1195 }
1196 }
1197
1208 public function unassignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup)
1209 {
1210 if (!$this->repository->canUser('class', 'update', $contentType, [$contentTypeGroup])) {
1211 throw new UnauthorizedException('ContentType', 'update');
1212 }
1213
1214 $spiContentType = $this->contentTypeHandler->load(
1215 $contentType->id,
1216 $contentType->status
1217 );
1218
1219 if (!in_array($contentTypeGroup->id, $spiContentType->groupIds)) {
1220 throw new InvalidArgumentException(
1221 '$contentTypeGroup',
1222 'The given ContentType is not assigned the ContentTypeGroup'
1223 );
1224 }
1225
1226 $this->repository->beginTransaction();
1227 try {
1228 $this->contentTypeHandler->unlink(
1229 $contentTypeGroup->id,
1230 $contentType->id,
1231 $contentType->status
1232 );
1233 $this->repository->commit();
1234 } catch (APIBadStateException $e) {
1235 $this->repository->rollback();
1236 throw new BadStateException(
1237 '$contentType',
1238 'The given ContentTypeGroup is the last group assigned to the ContentType',
1239 $e
1240 );
1241 } catch (Exception $e) {
1242 $this->repository->rollback();
1243 throw $e;
1244 }
1245 }
1246
1264 public function addFieldDefinition(APIContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct)
1265 {
1266 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1267 throw new UnauthorizedException('ContentType', 'update');
1268 }
1269
1270 $this->validateInputFieldDefinitionCreateStruct($fieldDefinitionCreateStruct);
1271 $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id);
1272
1273 if ($loadedContentTypeDraft->getFieldDefinition($fieldDefinitionCreateStruct->identifier) !== null) {
1274 throw new InvalidArgumentException(
1275 '$fieldDefinitionCreateStruct',
1276 "Another FieldDefinition with identifier '{$fieldDefinitionCreateStruct->identifier}' exists in the ContentType"
1277 );
1278 }
1279 //Fill default translations with default value for mainLanguageCode with fallback if no exist
1280 if (is_array($fieldDefinitionCreateStruct->names)) {
1281 foreach ($contentTypeDraft->languageCodes as $languageCode) {
1282 if (!array_key_exists($languageCode, $fieldDefinitionCreateStruct->names)) {
1283 $fieldDefinitionCreateStruct->names[$languageCode] = $fieldDefinitionCreateStruct->names[$contentTypeDraft->mainLanguageCode] ?? reset($fieldDefinitionCreateStruct->names);
1284 }
1285 }
1286 }
1287
1289 $fieldType = $this->fieldTypeRegistry->getFieldType(
1290 $fieldDefinitionCreateStruct->fieldTypeIdentifier
1291 );
1292
1293 $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings);
1294 $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration);
1295 $validationErrors = $this->validateFieldDefinitionCreateStruct($fieldDefinitionCreateStruct, $fieldType);
1296 if (!empty($validationErrors)) {
1297 $validationErrors = [$fieldDefinitionCreateStruct->identifier => $validationErrors];
1298 throw new ContentTypeFieldDefinitionValidationException($validationErrors);
1299 }
1300
1301 if ($fieldType->isSingular()) {
1302 foreach ($loadedContentTypeDraft->getFieldDefinitions() as $fieldDefinition) {
1303 if ($fieldDefinition->fieldTypeIdentifier === $fieldDefinitionCreateStruct->fieldTypeIdentifier) {
1304 throw new BadStateException(
1305 '$contentTypeDraft',
1306 "ContentType already contains field definition of non-repeatable field type '{$fieldDefinition->fieldTypeIdentifier}'"
1307 );
1308 }
1309 }
1310 }
1311
1312 if ($fieldType->onlyEmptyInstance() && $this->contentTypeHandler->getContentCount($loadedContentTypeDraft->id)
1313 ) {
1314 throw new BadStateException(
1315 '$contentTypeDraft',
1316 "Field definition of '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' field type cannot be added because ContentType has Content instances"
1317 );
1318 }
1319
1320 $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct(
1321 $fieldDefinitionCreateStruct,
1322 $fieldType,
1323 $contentTypeDraft->mainLanguageCode
1324 );
1325
1326 $this->repository->beginTransaction();
1327 try {
1328 $this->contentTypeHandler->addFieldDefinition(
1329 $contentTypeDraft->id,
1330 $contentTypeDraft->status,
1331 $spiFieldDefinition
1332 );
1333 $this->repository->commit();
1334 } catch (Exception $e) {
1335 $this->repository->rollback();
1336 throw $e;
1337 }
1338 }
1339
1349 public function removeFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition)
1350 {
1351 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1352 throw new UnauthorizedException('ContentType', 'update');
1353 }
1354
1355 $loadedFieldDefinition = $this->loadContentTypeDraft(
1356 $contentTypeDraft->id
1357 )->getFieldDefinition(
1358 $fieldDefinition->identifier
1359 );
1360
1361 if (empty($loadedFieldDefinition) || $loadedFieldDefinition->id != $fieldDefinition->id) {
1362 throw new InvalidArgumentException(
1363 '$fieldDefinition',
1364 'The given FieldDefinition does not belong to the ContentType'
1365 );
1366 }
1367
1368 $this->repository->beginTransaction();
1369 try {
1370 $this->contentTypeHandler->removeFieldDefinition(
1371 $contentTypeDraft->id,
1372 SPIContentType::STATUS_DRAFT,
1373 $fieldDefinition->id
1374 );
1375 $this->repository->commit();
1376 } catch (Exception $e) {
1377 $this->repository->rollback();
1378 throw $e;
1379 }
1380 }
1381
1393 public function updateFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct)
1394 {
1395 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1396 throw new UnauthorizedException('ContentType', 'update');
1397 }
1398
1399 $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id);
1400 $foundFieldId = false;
1401 foreach ($loadedContentTypeDraft->fieldDefinitions as $existingFieldDefinition) {
1402 if ($existingFieldDefinition->id == $fieldDefinition->id) {
1403 $foundFieldId = true;
1404 } elseif ($existingFieldDefinition->identifier == $fieldDefinitionUpdateStruct->identifier) {
1405 throw new InvalidArgumentException(
1406 '$fieldDefinitionUpdateStruct',
1407 "Another FieldDefinition with identifier '{$fieldDefinitionUpdateStruct->identifier}' exists in the ContentType"
1408 );
1409 }
1410 }
1411 if (!$foundFieldId) {
1412 throw new InvalidArgumentException(
1413 '$fieldDefinition',
1414 'The given FieldDefinition does not belong to the ContentType'
1415 );
1416 }
1417
1418 $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromUpdateStruct(
1419 $fieldDefinitionUpdateStruct,
1420 $fieldDefinition,
1421 $contentTypeDraft->mainLanguageCode
1422 );
1423
1424 $this->repository->beginTransaction();
1425 try {
1426 $this->contentTypeHandler->updateFieldDefinition(
1427 $contentTypeDraft->id,
1428 SPIContentType::STATUS_DRAFT,
1429 $spiFieldDefinition
1430 );
1431 $this->repository->commit();
1432 } catch (Exception $e) {
1433 $this->repository->rollback();
1434 throw $e;
1435 }
1436 }
1437
1449 public function publishContentTypeDraft(APIContentTypeDraft $contentTypeDraft)
1450 {
1451 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1452 throw new UnauthorizedException('ContentType', 'update');
1453 }
1454
1455 try {
1456 $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id);
1457 } catch (APINotFoundException $e) {
1458 throw new BadStateException(
1459 '$contentTypeDraft',
1460 'The content type does not have a draft.',
1461 $e
1462 );
1463 }
1464
1465 if (count($loadedContentTypeDraft->getFieldDefinitions()) === 0) {
1466 throw new InvalidArgumentException(
1467 '$contentTypeDraft',
1468 'The content type draft should have at least one field definition.'
1469 );
1470 }
1471
1472 $this->repository->beginTransaction();
1473 try {
1474 if (empty($loadedContentTypeDraft->nameSchema)) {
1475 $fieldDefinitions = $loadedContentTypeDraft->getFieldDefinitions();
1476 $this->contentTypeHandler->update(
1477 $contentTypeDraft->id,
1478 $contentTypeDraft->status,
1479 $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct(
1480 $loadedContentTypeDraft,
1481 new ContentTypeUpdateStruct(
1482 [
1483 'nameSchema' => '<' . $fieldDefinitions[0]->identifier . '>',
1484 ]
1485 ),
1486 $this->repository->getCurrentUserReference()
1487 )
1488 );
1489 }
1490
1491 $this->contentTypeHandler->publish(
1492 $loadedContentTypeDraft->id
1493 );
1494 $this->repository->commit();
1495 } catch (Exception $e) {
1496 $this->repository->rollback();
1497 throw $e;
1498 }
1499 }
1500
1510 public function newContentTypeGroupCreateStruct($identifier)
1511 {
1512 if (!is_string($identifier)) {
1513 throw new InvalidArgumentValue('$identifier', $identifier);
1514 }
1515
1516 return new ContentTypeGroupCreateStruct(
1517 [
1518 'identifier' => $identifier,
1519 ]
1520 );
1521 }
1522
1532 public function newContentTypeCreateStruct($identifier)
1533 {
1534 if (!is_string($identifier)) {
1535 throw new InvalidArgumentValue('$identifier', $identifier);
1536 }
1537
1538 return new ContentTypeCreateStruct(
1539 [
1540 'identifier' => $identifier,
1541 ]
1542 );
1543 }
1544
1550 public function newContentTypeUpdateStruct()
1551 {
1552 return new ContentTypeUpdateStruct();
1553 }
1554
1560 public function newContentTypeGroupUpdateStruct()
1561 {
1562 return new ContentTypeGroupUpdateStruct();
1563 }
1564
1576 public function newFieldDefinitionCreateStruct($identifier, $fieldTypeIdentifier)
1577 {
1578 if (!is_string($identifier)) {
1579 throw new InvalidArgumentValue('$identifier', $identifier);
1580 }
1581
1582 if (!is_string($fieldTypeIdentifier)) {
1583 throw new InvalidArgumentValue('$fieldTypeIdentifier', $fieldTypeIdentifier);
1584 }
1585
1586 return new FieldDefinitionCreateStruct(
1587 [
1588 'identifier' => $identifier,
1589 'fieldTypeIdentifier' => $fieldTypeIdentifier,
1590 ]
1591 );
1592 }
1593
1599 public function newFieldDefinitionUpdateStruct()
1600 {
1601 return new FieldDefinitionUpdateStruct();
1602 }
1603
1613 public function isContentTypeUsed(APIContentType $contentType)
1614 {
1615 return $this->contentTypeHandler->getContentCount($contentType->id) > 0;
1616 }
1617
1628 public function removeContentTypeTranslation(APIContentTypeDraft $contentTypeDraft, string $languageCode): APIContentTypeDraft
1629 {
1630 if (!$this->repository->canUser('class', 'update', $contentTypeDraft)) {
1631 throw new UnauthorizedException('ContentType', 'update');
1632 }
1633
1634 $this->repository->beginTransaction();
1635 try {
1636 $contentType = $this->contentTypeHandler->removeContentTypeTranslation(
1637 $contentTypeDraft->id,
1638 $languageCode
1639 );
1640
1641 $this->repository->commit();
1642 } catch (Exception $e) {
1643 $this->repository->rollback();
1644 throw $e;
1645 }
1646
1647 return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($contentType);
1648 }
1649
1650 public function deleteUserDrafts(int $userId): void
1651 {
1652 try {
1653 $this->userHandler->load($userId);
1654 } catch (APINotFoundException $e) {
1655 $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT);
1656
1657 return;
1658 }
1659
1660 if ($this->repository->getPermissionResolver()->hasAccess('class', 'delete') !== true) {
1661 throw new UnauthorizedException('ContentType', 'update');
1662 }
1663
1664 $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT);
1665 }
1666 }
eZ\Publish\API\Repository\Exceptions\BadStateException
This Exception is thrown if a method is called with an value referencing an object which is not in th...
Definition: API/Repository/Exceptions/BadStateException.php:13
eZ\Publish\API\Repository\Exceptions\NotFoundException
This Exception is thrown if an object referenced by an id or identifier could not be found in the rep...
Definition: API/Repository/Exceptions/NotFoundException.php:16
eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct
Definition: API/Repository/Values/ContentType/ContentTypeCreateStruct.php:18
eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft
This class represents a draft of a content type.
Definition: API/Repository/Values/ContentType/ContentTypeDraft.php:13
eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct
This class is used for creating a content type group.
Definition: API/Repository/Values/ContentType/ContentTypeGroupCreateStruct.php:13
eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct
This class is used for updating a content type group.
Definition: API/Repository/Values/ContentType/ContentTypeGroupUpdateStruct.php:13
eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup
Definition: Publish/API/Repository/Values/ContentType/ContentTypeGroup.php:24
eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct
This class is used for updating a content type.
Definition: ContentTypeUpdateStruct.php:15
eZ\Publish\API\Repository\Values\ContentType\ContentType
Definition: API/Repository/Values/ContentType/ContentType.php:36
eZ\Publish\API\Repository\Values\ContentType\ContentType\STATUS_DRAFT
const STATUS_DRAFT
Definition: API/Repository/Values/ContentType/ContentType.php:41
eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct
this class is used to create a field definition.
Definition: API/Repository/Values/ContentType/FieldDefinitionCreateStruct.php:15
eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct
this class is used to update a field definition.
Definition: FieldDefinitionUpdateStruct.php:15
eZ\Publish\API\Repository\Values\ContentType\FieldDefinition
Definition: API/Repository/Values/ContentType/FieldDefinition.php:31
eZ\Publish\API\Repository\Values\Content\Location
Definition: API/Repository/Values/Content/Location.php:32
eZ\Publish\API\Repository\Values\Content\Location\SORT_ORDER_DESC
const SORT_ORDER_DESC
Definition: API/Repository/Values/Content/Location.php:52
eZ\Publish\API\Repository\Values\Content\Location\SORT_FIELD_PUBLISHED
const SORT_FIELD_PUBLISHED
Definition: API/Repository/Values/Content/Location.php:35
eZ\Publish\API\Repository\Values\User\User
Definition: Publish/API/Repository/Values/User/User.php:24
eZ\Publish\Core\Base\Exceptions\BadStateException
BadState Exception implementation.
Definition: Core/Base/Exceptions/BadStateException.php:20
eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException
This Exception is thrown on create or update content one or more given fields are not valid.
Definition: Core/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php:17
eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException
This Exception is thrown on create or update content type when content type is not valid.
Definition: Core/Base/Exceptions/ContentTypeValidationException.php:17
eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
Invalid Argument Type Exception implementation.
Definition: Core/Base/Exceptions/InvalidArgumentException.php:20
eZ\Publish\Core\Base\Exceptions\InvalidArgumentType
Invalid Argument Type Exception implementation.
Definition: InvalidArgumentType.php:17
eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue
Invalid Argument Type Exception implementation.
Definition: Base/Exceptions/InvalidArgumentValue.php:17
eZ\Publish\Core\Base\Exceptions\NotFoundException
Not Found Exception implementation.
Definition: Core/Base/Exceptions/NotFoundException.php:21
eZ\Publish\Core\Base\Exceptions\UnauthorizedException
UnauthorizedException Exception implementation.
Definition: Core/Base/Exceptions/UnauthorizedException.php:21
eZ\Publish\Core\FieldType\ValidationError
Class for validation errors.
Definition: Core/FieldType/ValidationError.php:17
eZ\Publish\Core\Repository\ContentTypeService\newContentTypeGroupCreateStruct
newContentTypeGroupCreateStruct($identifier)
Instantiates a new content type group create class.
Definition: Core/Repository/ContentTypeService.php:1510
eZ\Publish\Core\Repository\ContentTypeService\validateInputFieldDefinitionCreateStruct
validateInputFieldDefinitionCreateStruct(FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, $argumentName=' $fieldDefinitionCreateStruct')
Validates input FieldDefinitionCreateStruct.
Definition: Core/Repository/ContentTypeService.php:502
eZ\Publish\Core\Repository\ContentTypeService\newFieldDefinitionCreateStruct
newFieldDefinitionCreateStruct($identifier, $fieldTypeIdentifier)
Instantiates a field definition create struct.
Definition: Core/Repository/ContentTypeService.php:1576
eZ\Publish\Core\Repository\ContentTypeService\newContentTypeGroupUpdateStruct
newContentTypeGroupUpdateStruct()
Instantiates a new content type update struct.
Definition: Core/Repository/ContentTypeService.php:1560
eZ\Publish\Core\Repository\ContentTypeService\loadContentType
loadContentType($contentTypeId, array $prioritizedLanguages=[])
{Get a Content Type object by id.}
Definition: Core/Repository/ContentTypeService.php:844
eZ\Publish\Core\Repository\ContentTypeService\updateContentTypeGroup
updateContentTypeGroup(APIContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct)
Update a Content Type Group object.
Definition: Core/Repository/ContentTypeService.php:216
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeGroupByIdentifier
loadContentTypeGroupByIdentifier($contentTypeGroupIdentifier, array $prioritizedLanguages=[])
{Get a Content Type Group object by identifier.}
Definition: Core/Repository/ContentTypeService.php:179
eZ\Publish\Core\Repository\ContentTypeService\removeContentTypeTranslation
removeContentTypeTranslation(APIContentTypeDraft $contentTypeDraft, string $languageCode)
Definition: Core/Repository/ContentTypeService.php:1628
eZ\Publish\Core\Repository\ContentTypeService\newContentTypeUpdateStruct
newContentTypeUpdateStruct()
Instantiates a new content type update struct.
Definition: Core/Repository/ContentTypeService.php:1550
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeGroup
loadContentTypeGroup($contentTypeGroupId, array $prioritizedLanguages=[])
{Get a Content Type Group object by id.}
Definition: Core/Repository/ContentTypeService.php:167
eZ\Publish\Core\Repository\ContentTypeService\createContentTypeGroup
createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct)
Create a Content Type Group object.
Definition: Core/Repository/ContentTypeService.php:111
eZ\Publish\Core\Repository\ContentTypeService\copyContentType
copyContentType(APIContentType $contentType, User $creator=null)
Copy Type incl fields and groupIds to a new Type object.
Definition: Core/Repository/ContentTypeService.php:1131
eZ\Publish\Core\Repository\ContentTypeService\$userHandler
$userHandler
Definition: Core/Repository/ContentTypeService.php:55
eZ\Publish\Core\Repository\ContentTypeService\$settings
$settings
Definition: Core/Repository/ContentTypeService.php:58
eZ\Publish\Core\Repository\ContentTypeService\$contentTypeHandler
$contentTypeHandler
Definition: Core/Repository/ContentTypeService.php:52
eZ\Publish\Core\Repository\ContentTypeService\updateContentTypeDraft
updateContentTypeDraft(APIContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct)
Update a Content Type object.
Definition: Core/Repository/ContentTypeService.php:1013
eZ\Publish\Core\Repository\ContentTypeService\deleteContentType
deleteContentType(APIContentType $contentType)
Delete a Content Type object.
Definition: Core/Repository/ContentTypeService.php:1091
eZ\Publish\Core\Repository\ContentTypeService\validateInputContentTypeGroups
validateInputContentTypeGroups(array $contentTypeGroups)
Validates input ContentTypeGroup array.
Definition: Core/Repository/ContentTypeService.php:472
eZ\Publish\Core\Repository\ContentTypeService\__construct
__construct(RepositoryInterface $repository, Handler $contentTypeHandler, UserHandler $userHandler, Helper\DomainMapper $domainMapper, Helper\ContentTypeDomainMapper $contentTypeDomainMapper, Helper\FieldTypeRegistry $fieldTypeRegistry, array $settings=[])
Setups service with reference to repository object that created it & corresponding handler.
Definition: Core/Repository/ContentTypeService.php:80
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeByIdentifier
loadContentTypeByIdentifier($identifier, array $prioritizedLanguages=[])
{Get a Content Type object by identifier.}
Definition: Core/Repository/ContentTypeService.php:860
eZ\Publish\Core\Repository\ContentTypeService\$contentTypeDomainMapper
$contentTypeDomainMapper
Definition: Core/Repository/ContentTypeService.php:64
eZ\Publish\Core\Repository\ContentTypeService\isContentTypeUsed
isContentTypeUsed(APIContentType $contentType)
Returns true if the given content type $contentType has content instances.
Definition: Core/Repository/ContentTypeService.php:1613
eZ\Publish\Core\Repository\ContentTypeService\$fieldTypeRegistry
$fieldTypeRegistry
Definition: Core/Repository/ContentTypeService.php:67
eZ\Publish\Core\Repository\ContentTypeService\assignContentTypeGroup
assignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup)
Assigns a content type to a content type group.
Definition: Core/Repository/ContentTypeService.php:1166
eZ\Publish\Core\Repository\ContentTypeService\createContentTypeDraft
createContentTypeDraft(APIContentType $contentType)
Creates a draft from an existing content type.
Definition: Core/Repository/ContentTypeService.php:968
eZ\Publish\Core\Repository\ContentTypeService\validateInputContentTypeCreateStruct
validateInputContentTypeCreateStruct(APIContentTypeCreateStruct $contentTypeCreateStruct)
Validates input ContentType create struct.
Definition: Core/Repository/ContentTypeService.php:315
eZ\Publish\Core\Repository\ContentTypeService\deleteContentTypeGroup
deleteContentTypeGroup(APIContentTypeGroup $contentTypeGroup)
Delete a Content Type Group.
Definition: Core/Repository/ContentTypeService.php:279
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeGroups
loadContentTypeGroups(array $prioritizedLanguages=[])
{Get all Content Type Groups.Used as prioritized language code on translated properties of returned o...
Definition: Core/Repository/ContentTypeService.php:195
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeByRemoteId
loadContentTypeByRemoteId($remoteId, array $prioritizedLanguages=[])
{Get a Content Type object by id.}
Definition: Core/Repository/ContentTypeService.php:879
eZ\Publish\Core\Repository\ContentTypeService\updateFieldDefinition
updateFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct)
Update a field definition.
Definition: Core/Repository/ContentTypeService.php:1393
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeDraft
loadContentTypeDraft($contentTypeId, bool $ignoreOwnership=false)
Get a Content Type object draft by id.
Definition: Core/Repository/ContentTypeService.php:901
eZ\Publish\Core\Repository\ContentTypeService\removeFieldDefinition
removeFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition)
Remove a field definition from an existing Type.
Definition: Core/Repository/ContentTypeService.php:1349
eZ\Publish\Core\Repository\ContentTypeService\$domainMapper
$domainMapper
Definition: Core/Repository/ContentTypeService.php:61
eZ\Publish\Core\Repository\ContentTypeService\deleteUserDrafts
deleteUserDrafts(int $userId)
Delete all content type drafs created or modified by the user.
Definition: Core/Repository/ContentTypeService.php:1650
eZ\Publish\Core\Repository\ContentTypeService\unassignContentTypeGroup
unassignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup)
Unassign a content type from a group.
Definition: Core/Repository/ContentTypeService.php:1208
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypes
loadContentTypes(APIContentTypeGroup $contentTypeGroup, array $prioritizedLanguages=[])
{}
Definition: Core/Repository/ContentTypeService.php:937
eZ\Publish\Core\Repository\ContentTypeService\loadContentTypeList
loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages=[])
{Bulk-load Content Type objects by ids.Note: it does not throw exceptions on load,...
Definition: Core/Repository/ContentTypeService.php:918
eZ\Publish\Core\Repository\ContentTypeService\newFieldDefinitionUpdateStruct
newFieldDefinitionUpdateStruct()
Instantiates a field definition update class.
Definition: Core/Repository/ContentTypeService.php:1599
eZ\Publish\Core\Repository\ContentTypeService\$repository
$repository
Definition: Core/Repository/ContentTypeService.php:49
eZ\Publish\Core\Repository\ContentTypeService\validateFieldDefinitionCreateStruct
validateFieldDefinitionCreateStruct(FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, SPIFieldType $fieldType)
Validates FieldDefinitionCreateStruct.
Definition: Core/Repository/ContentTypeService.php:824
eZ\Publish\Core\Repository\ContentTypeService\newContentTypeCreateStruct
newContentTypeCreateStruct($identifier)
Instantiates a new content type create class.
Definition: Core/Repository/ContentTypeService.php:1532
eZ\Publish\Core\Repository\ContentTypeService\publishContentTypeDraft
publishContentTypeDraft(APIContentTypeDraft $contentTypeDraft)
Publish the content type and update content objects.
Definition: Core/Repository/ContentTypeService.php:1449
eZ\Publish\Core\Repository\Values\ContentType\ContentTypeCreateStruct
Definition: Core/Repository/Values/ContentType/ContentTypeCreateStruct.php:20
eZ\Publish\SPI\Persistence\Content\Type\CreateStruct
Definition: SPI/Persistence/Content/Type/CreateStruct.php:13
eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct
Definition: SPI/Persistence/Content/Type/Group/CreateStruct.php:12
eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct
Definition: Content/Type/Group/UpdateStruct.php:12
eZ\Publish\API\Repository\ContentTypeService
Definition: API/Repository/ContentTypeService.php:25
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeDraft
loadContentTypeDraft($contentTypeId, bool $ignoreOwnership=false)
Get a Content Type object draft by id.
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeGroup
loadContentTypeGroup($contentTypeGroupId, array $prioritizedLanguages=[])
Get a Content Type Group object by id.
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeByRemoteId
loadContentTypeByRemoteId($remoteId, array $prioritizedLanguages=[])
Get a Content Type object by id.
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeGroupByIdentifier
loadContentTypeGroupByIdentifier($contentTypeGroupIdentifier, array $prioritizedLanguages=[])
Get a Content Type Group object by identifier.
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeByIdentifier
loadContentTypeByIdentifier($identifier, array $prioritizedLanguages=[])
Get a Content Type object by identifier.
eZ\Publish\API\Repository\ContentTypeService\addFieldDefinition
addFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct)
Adds a new field definition to an existing content type.
eZ\Publish\API\Repository\ContentTypeService\loadContentTypeGroups
loadContentTypeGroups(array $prioritizedLanguages=[])
Get all Content Type Groups.
eZ\Publish\API\Repository\ContentTypeService\createContentType
createContentType(ContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups)
Create a Content Type object.
eZ\Publish\API\Repository\ContentTypeService\loadContentType
loadContentType($contentTypeId, array $prioritizedLanguages=[])
Get a Content Type object by id.
eZ\Publish\API\Repository\Repository
Repository interface.
Definition: API/Repository/Repository.php:16
eZ\Publish\SPI\FieldType\FieldType
The field type interface which all field types have to implement.
Definition: SPI/FieldType/FieldType.php:30
eZ\Publish\SPI\Persistence\Content\Language\Handler
Language Handler interface.
Definition: SPI/Persistence/Content/Language/Handler.php:15
eZ\Publish\SPI\Persistence\Content\Type\Handler
Definition: SPI/Persistence/Content/Type/Handler.php:14
eZ\Publish\SPI\Persistence\User\Handler
Storage Engine handler for user module.
Definition: SPI/Persistence/User/Handler.php:15
eZ\Publish\Core\Repository
Definition: Core/Repository/BookmarkService.php:9
eZ\Publish\SPI\Persistence\Content\Type
Definition: SPI/Persistence/Content/Type/CreateStruct.php:7
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.