diff --git a/API/ComplexFieldInterface.php b/API/ComplexFieldInterface.php index 06a52858..a8725bb7 100644 --- a/API/ComplexFieldInterface.php +++ b/API/ComplexFieldInterface.php @@ -8,10 +8,11 @@ interface ComplexFieldInterface { /** - * Return a non primitive field value - * @param array $fieldValueArray The definition of teh field value, structured in the yml file + * Return a non primitive field value - or a primitive field value after further transformation + * + * @param $fieldValueArray The definition of the field value, as used in the yml file * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return mixed */ - public function createValue(array $fieldValueArray, array $context = array()); -} \ No newline at end of file + public function createValue($fieldValueArray, array $context = array()); +} diff --git a/Core/ComplexField/ComplexFieldManager.php b/Core/ComplexField/ComplexFieldManager.php index ebf21afa..47101347 100644 --- a/Core/ComplexField/ComplexFieldManager.php +++ b/Core/ComplexField/ComplexFieldManager.php @@ -18,30 +18,48 @@ public function __construct(FieldTypeService $fieldTypeService) /** * @param ComplexFieldInterface $complexField + * @param string $fieldTypeIdentifier * @param string $contentTypeIdentifier */ - public function addComplexField(ComplexFieldInterface $complexField, $contentTypeIdentifier) + public function addComplexField(ComplexFieldInterface $complexField, $fieldTypeIdentifier, $contentTypeIdentifier=null) { - $this->fieldTypeMap[$contentTypeIdentifier] = $complexField; + if ($contentTypeIdentifier == null) { + $contentTypeIdentifier = '*'; + } + $this->fieldTypeMap[$contentTypeIdentifier][$fieldTypeIdentifier] = $complexField; + } + + /** + * @param $fieldTypeIdentifier + * @param string $contentTypeIdentifier + * @return bool + */ + public function managesField($fieldTypeIdentifier, $contentTypeIdentifier) + { + return (isset($this->fieldTypeMap[$contentTypeIdentifier][$fieldTypeIdentifier]) || + isset($this->fieldTypeMap['*'][$fieldTypeIdentifier])); } /** * @param string $fieldTypeIdentifier - * @param array $fieldValueArray + * @param string $contentTypeIdentifier + * @param mixed $fieldValue * @param array $context - * @return ComplexFieldInterface + * @return mixed */ - public function getComplexFieldValue($fieldTypeIdentifier, array $fieldValueArray, array $context = array()) + public function getComplexFieldValue($fieldTypeIdentifier, $contentTypeIdentifier, $fieldValue, array $context = array()) { - if (array_key_exists($fieldTypeIdentifier, $this->fieldTypeMap)) + if (isset($this->fieldTypeMap[$contentTypeIdentifier][$fieldTypeIdentifier]) || isset($this->fieldTypeMap['*'][$fieldTypeIdentifier])) { - $fieldService = $this->fieldTypeMap[$fieldTypeIdentifier]; - return $fieldService->createValue($fieldValueArray, $context); + $fieldService = isset($this->fieldTypeMap[$contentTypeIdentifier][$fieldTypeIdentifier]) ? + $this->fieldTypeMap[$contentTypeIdentifier][$fieldTypeIdentifier] : + $this->fieldTypeMap['*'][$fieldTypeIdentifier]; + return $fieldService->createValue($fieldValue, $context); } else { $fieldType = $this->fieldTypeService->getFieldType($fieldTypeIdentifier); - return $fieldType->fromHash($fieldValueArray); + return $fieldType->fromHash($fieldValue); // was: error //throw new \InvalidArgumentException("Field of type '$fieldTypeIdentifier' can not be handled as it does not have a complex field class defined"); } diff --git a/Core/ComplexField/EzAuthor.php b/Core/ComplexField/EzAuthor.php index 031a0824..fb0e5669 100644 --- a/Core/ComplexField/EzAuthor.php +++ b/Core/ComplexField/EzAuthor.php @@ -18,7 +18,7 @@ class EzAuthor extends AbstractComplexField implements ComplexFieldInterface * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return AuthorValue */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $authors = array(); diff --git a/Core/ComplexField/EzBinaryFile.php b/Core/ComplexField/EzBinaryFile.php index 825aa88a..d1d1b490 100644 --- a/Core/ComplexField/EzBinaryFile.php +++ b/Core/ComplexField/EzBinaryFile.php @@ -12,7 +12,7 @@ class EzBinaryFile extends AbstractComplexField implements ComplexFieldInterface * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return BinaryFileValue */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $filePath = dirname($context['path']) . '/files/' . $fieldValueArray['path']; diff --git a/Core/ComplexField/EzImage.php b/Core/ComplexField/EzImage.php index aa7acb97..e0e3494e 100644 --- a/Core/ComplexField/EzImage.php +++ b/Core/ComplexField/EzImage.php @@ -14,7 +14,7 @@ class EzImage extends AbstractComplexField implements ComplexFieldInterface * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return ImageValue */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $filePath = dirname($context['path']) . '/images/' . $fieldValueArray['path']; $altText = array_key_exists('alt_text', $fieldValueArray) ? $fieldValueArray['alt_text'] : ''; diff --git a/Core/ComplexField/EzPage.php b/Core/ComplexField/EzPage.php index 61e1f881..8355833c 100644 --- a/Core/ComplexField/EzPage.php +++ b/Core/ComplexField/EzPage.php @@ -26,7 +26,7 @@ public function __construct(PageService $pageService) * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return PageValue */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $layout = $fieldValueArray['layout']; diff --git a/Core/ComplexField/EzRelation.php b/Core/ComplexField/EzRelation.php index 56e3438b..078f31b4 100644 --- a/Core/ComplexField/EzRelation.php +++ b/Core/ComplexField/EzRelation.php @@ -22,14 +22,18 @@ public function __construct(ContentMatcher $contentMatcher) * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return Value */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { - $id = $fieldValueArray['destinationContentId']; + if (count($fieldValueArray) == 1 && isset($fieldValueArray['destinationContentId'])) { + // fromHash format + $id = $fieldValueArray['destinationContentId']; + } else { + // simplified format + $id = $fieldValueArray; + } // 1. resolve relations - if ($this->referenceResolver->isReference($id)) { - $id = $this->referenceResolver->getReferenceValue($id); - } + $id = $this->referenceResolver->resolveReference($id); // 2. resolve remote ids $id = $this->contentMatcher->matchOneByKey($id)->id; diff --git a/Core/ComplexField/EzRelationList.php b/Core/ComplexField/EzRelationList.php index 0b8b9cfb..df6426cb 100644 --- a/Core/ComplexField/EzRelationList.php +++ b/Core/ComplexField/EzRelationList.php @@ -20,7 +20,7 @@ public function __construct(ContentMatcher $contentMatcher) * @param array $context The context for execution of the current migrations. Contains f.e. the path to the migration * @return Value */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { if (count($fieldValueArray) == 1 && isset($fieldValueArray['destinationContentIds'])) { // fromHash format @@ -32,9 +32,7 @@ public function createValue(array $fieldValueArray, array $context = array()) foreach ($ids as $key => $id) { // 1. resolve relations - if ($this->referenceResolver->isReference($id)) { - $ids[$key] = $this->referenceResolver->getReferenceValue($id); - } + $ids[$key] = $this->referenceResolver->getReferenceValue($id); // 2. resolve remote ids $ids[$key] = $this->contentMatcher->matchOneByKey($ids[$key])->id; } diff --git a/Core/ComplexField/EzRichText.php b/Core/ComplexField/EzRichText.php index e6d7d772..bdd11564 100644 --- a/Core/ComplexField/EzRichText.php +++ b/Core/ComplexField/EzRichText.php @@ -26,7 +26,7 @@ public function __construct(ContentMatcher $contentMatcher, LocationMatcher $loc * * @todo replace objects and location refs in ezcontent:// and ezlocation:// links */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $xmlText = $fieldValueArray['content']; diff --git a/Core/ComplexField/EzXmlText.php b/Core/ComplexField/EzXmlText.php index 856c4869..778c373a 100644 --- a/Core/ComplexField/EzXmlText.php +++ b/Core/ComplexField/EzXmlText.php @@ -26,7 +26,7 @@ public function __construct(ContentMatcher $contentMatcher, LocationMatcher $loc * * @todo replace objects and location refs in eznode and ezobject links */ - public function createValue(array $fieldValueArray, array $context = array()) + public function createValue($fieldValueArray, array $context = array()) { $xmlText = $fieldValueArray['content']; diff --git a/Core/Executor/ContentManager.php b/Core/Executor/ContentManager.php index 5274eb63..7e20d3b8 100644 --- a/Core/Executor/ContentManager.php +++ b/Core/Executor/ContentManager.php @@ -54,6 +54,7 @@ protected function create() $contentTypeIdentifier = $this->dsl['content_type']; $contentTypeIdentifier = $this->referenceResolver->resolveReference($contentTypeIdentifier); + /// @todo use a contenttypematcher $contentType = $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); $contentCreateStruct = $contentService->newContentCreateStruct($contentType, $this->getLanguageCode()); @@ -284,13 +285,7 @@ protected function setFields($createOrUpdateStruct, array $fields, ContentType $ throw new \Exception("Field '$fieldIdentifier' is not present in field type '{$contentType->identifier}'"); } - if (is_array($field[$fieldIdentifier])) { - // Complex field might need special handling eg.: ezimage, ezbinaryfile - $fieldValue = $this->getComplexFieldValue($field[$fieldIdentifier], $fieldType, $this->context); - } else { - // Primitive field eg.: ezstring, ezxml etc. - $fieldValue = $this->getSingleFieldValue($field[$fieldIdentifier], $fieldType, $this->context); - } + $fieldValue = $this->getFieldValue($field[$fieldIdentifier], $fieldType, $contentType->identifier, $this->context); $createOrUpdateStruct->setField($fieldIdentifier, $fieldValue, $this->getLanguageCode()); } @@ -317,19 +312,41 @@ protected function setObjectStates(Content $content, array $stateKeys) } } + /** + * Create the field value for either a primitive or complex field + * + * @param mixed $value + * @param FieldDefinition $fieldDefinition + * @param string $contentTypeIdentifier + * @param array $context + * @throws \InvalidArgumentException + * @return mixed + */ + protected function getFieldValue($value, FieldDefinition $fieldDefinition, $contentTypeIdentifier, array $context = array()) + { + $fieldTypeIdentifier = $fieldDefinition->fieldTypeIdentifier; + if (is_array($value) || $this->complexFieldManager->managesField($fieldTypeIdentifier, $contentTypeIdentifier)) { + return $this->complexFieldManager->getComplexFieldValue($fieldTypeIdentifier, $contentTypeIdentifier, $value, $context); + } + + return $this->getSingleFieldValue($value, $fieldDefinition, $contentTypeIdentifier, $context); + } + /** * Create the field value for a primitive field * This function is needed to get past validation on Checkbox fieldtype (eZP bug) * * @param mixed $value * @param FieldDefinition $fieldDefinition + * @param string $contentTypeIdentifier * @param array $context * @throws \InvalidArgumentException - * @return object + * @return mixed */ - protected function getSingleFieldValue($value, FieldDefinition $fieldDefinition, array $context = array()) + protected function getSingleFieldValue($value, FieldDefinition $fieldDefinition, $contentTypeIdentifier, array $context = array()) { - switch ($fieldDefinition->fieldTypeIdentifier) { + $fieldTypeIdentifier = $fieldDefinition->fieldTypeIdentifier; + switch ($fieldTypeIdentifier) { case 'ezboolean': $value = new CheckboxValue(($value == 1) ? true : false); break; @@ -337,25 +354,12 @@ protected function getSingleFieldValue($value, FieldDefinition $fieldDefinition, // do nothing } - // q: do we really want this ? - $value = $this->referenceResolver->resolveReference($value); + // We do not really want this to happen by default on all scalar field values. If you want this to happen, register a complex field for it + //$value = $this->referenceResolver->resolveReference($value); return $value; } - /** - * Create the field value for a complex field eg.: ezimage, ezfile - * - * @param FieldDefinition $fieldDefinition - * @param array $fieldValueArray - * @param array $context - * @return object - */ - protected function getComplexFieldValue(array $fieldValueArray, FieldDefinition $fieldDefinition, array $context = array()) - { - return $this->complexFieldManager->getComplexFieldValue($fieldDefinition->fieldTypeIdentifier, $fieldValueArray, $context); - } - /** * Load user using either login, email, id - resolving eventual references * @param int|string $userKey diff --git a/DependencyInjection/CompilerPass/TaggedServicesCompilerPass.php b/DependencyInjection/CompilerPass/TaggedServicesCompilerPass.php index 83e1c7f2..8a9939ab 100644 --- a/DependencyInjection/CompilerPass/TaggedServicesCompilerPass.php +++ b/DependencyInjection/CompilerPass/TaggedServicesCompilerPass.php @@ -39,7 +39,8 @@ public function process(ContainerBuilder $container) foreach ($tags as $attributes) { $migrationService->addMethodCall('addComplexField', array( new Reference($id), - $attributes['fieldtype'] + $attributes['fieldtype'], + isset($attributes['contenttype']) ? $attributes['contenttype'] : null )); } } diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 4577c6d1..f0c697b0 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -271,14 +271,15 @@ services: class: '%ez_migration_bundle.complex_field_manager.class%' arguments: - '@ezpublish.api.service.field_type' - # - '@ezpublish.api.repository' - # - '@service_container' ez_migration_bundle.complex_field: abstract: true calls: - ['setReferenceResolver', ['@ez_migration_bundle.reference_resolver.customreference']] + # NB: you can register handlers for either a field type, using the 'fieldtype' attribute, or for the + # field type of a specific content type by using both 'fieldtype' and 'contenttype' in the tag definition + ez_migration_bundle.complex_field.ezauthor: parent: ez_migration_bundle.complex_field class: '%ez_migration_bundle.complex_field.ezauthor.class%' diff --git a/WHATSNEW.md b/WHATSNEW.md index b775ad4d..faf3da9d 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -34,11 +34,18 @@ Version 3.0.0 For an example, see the test UnitTestOK010_customrefs.yml and class Kaliop\eZMigrationBundle\Tests\helper\CustomReferenceResolver +* New: it is now possible to inject field type handlers for scalar field types, as well as for field type handlers + that only affect the field type of a single content type. + This gives greater flexibility in deciding which types of references are resolved for each field when creating + or updating contents + * Changed: removed unused Behat and Phpunit tests as well as one leftover Interface * Changed: removed from the YML documentation the description of keys which had been deprecated in version 2. The keys are still accepted, but support for them will be dropped in a future version +* Changed: the service ez_migration_bundle.reference_resolver.content is now deprecated and will be removed in a future version + * Changes to the YML definition language: * Updating and deleting of Users, User Groups, Roles and Content Types: usage of a `match` key is allowed; previous @@ -64,9 +71,21 @@ Version 3.0.0 * when adding locations without defining sort field and sort order, the defaults from the content type definition are used, instead of publication-date/asc - * changes for developers who extended the bundle: the MatcherInterface has a new method; many Executor services - now have a different constructor signature; one Trait has been removed from the public API; the service - ez_migration_bundle.helper.role_handler has been renamed to ez_migration_bundle.helper.limitation_converter + * references to 'tag:' and 'location:' will not be resolved any more in fields of type Object Relation and + Object Relation List. On the other hand non-integer strings will be resolved as remote-content-ids + + * custom references are not resolved any more in the values for scalar fields when creating/updating content. + The old behaviour was inconsistent, as references were resolved by default for scalar values but not for + array values. + If you need to have reference resolution in your content field values, you will have to register a custom field + type handler using a tagged service (see services.yml for how the current handlers do it) and do it yourself + + * changes for developers who extended the bundle: the MatcherInterface and ReferenceResolverInterface have a new + method each; many Executor services now have a different constructor signature; one Trait has been removed from + the public API; the service ez_migration_bundle.helper.role_handler has been renamed + to ez_migration_bundle.helper.limitation_converter; the chainResolver will now apply reference transformation + from all matching sub-resolvers, not only from the 1st one + Version 2.1.0 =============