Skip to content

Commit

Permalink
Allow registration of Complex Field handlers even for scalar fields; …
Browse files Browse the repository at this point in the history
…Allow registration of Complex Field handlers even for scalar fields for specific content types
  • Loading branch information
gggeek committed Oct 5, 2016
1 parent df5eda1 commit 4739c9a
Show file tree
Hide file tree
Showing 14 changed files with 105 additions and 59 deletions.
9 changes: 5 additions & 4 deletions API/ComplexFieldInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
public function createValue($fieldValueArray, array $context = array());
}
36 changes: 27 additions & 9 deletions Core/ComplexField/ComplexFieldManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzAuthor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzBinaryFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'] : '';
Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand Down
14 changes: 9 additions & 5 deletions Core/ComplexField/EzRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 2 additions & 4 deletions Core/ComplexField/EzRelationList.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzRichText.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand Down
2 changes: 1 addition & 1 deletion Core/ComplexField/EzXmlText.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand Down
54 changes: 29 additions & 25 deletions Core/Executor/ContentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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());
}
Expand All @@ -317,45 +312,54 @@ 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;
default:
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
));
}
}
Expand Down
5 changes: 3 additions & 2 deletions Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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%'
Expand Down
25 changes: 22 additions & 3 deletions WHATSNEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
=============
Expand Down

0 comments on commit 4739c9a

Please sign in to comment.