diff --git a/jsonapi_extras.services.yml b/jsonapi_extras.services.yml index 44131f6c63018d5d2901f6b007d6423430ac2ecc..d8795bd3d4c6551acd6035cba419c6e92c060f90 100644 --- a/jsonapi_extras.services.yml +++ b/jsonapi_extras.services.yml @@ -16,6 +16,17 @@ services: tags: - { name: normalizer, priority: 25 } + serializer.normalizer.entity.jsonapi_extras: + class: Drupal\jsonapi_extras\Normalizer\ContentEntityNormalizer + arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager'] + tags: + - { name: normalizer, priority: 22 } + serializer.normalizer.config_entity.jsonapi_extras: + class: Drupal\jsonapi_extras\Normalizer\ConfigEntityNormalizer + arguments: ['@jsonapi.link_manager', '@jsonapi.resource_type.repository', '@entity_type.manager'] + tags: + - { name: normalizer, priority: 22 } + plugin.manager.resource_field_enhancer: class: Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager parent: default_plugin_manager diff --git a/src/JsonapiExtrasServiceProvider.php b/src/JsonapiExtrasServiceProvider.php index ce81c4d05c901079b060fdb874c25652defe4ba2..52c551898253487e2c2bf8d7310cafe9ff547391 100644 --- a/src/JsonapiExtrasServiceProvider.php +++ b/src/JsonapiExtrasServiceProvider.php @@ -20,8 +20,10 @@ class JsonapiExtrasServiceProvider implements ServiceModifierInterface { // Override the class used for the configurable service. $definition = $container->getDefinition('jsonapi.resource_type.repository'); $definition->setClass(ConfigurableResourceTypeRepository::class); - // The configurable service expects the entity repository. + // The configurable service expects the entity repository and the enhancer + // plugin manager. $definition->addArgument(new Reference('entity.repository')); + $definition->addArgument(new Reference('plugin.manager.resource_field_enhancer')); } } diff --git a/src/Normalizer/ConfigEntityNormalizer.php b/src/Normalizer/ConfigEntityNormalizer.php new file mode 100644 index 0000000000000000000000000000000000000000..2bd398c74c43663f45fe40c484d2a7a69c6e234c --- /dev/null +++ b/src/Normalizer/ConfigEntityNormalizer.php @@ -0,0 +1,14 @@ + $field_value) { + // Skip any disabled field. + if (!$resource_type->isFieldEnabled($public_field_name)) { + continue; + } + $internal_name = $resource_type->getInternalName($public_field_name); + $enhancer = $resource_type->getFieldEnhancer($public_field_name, 'publicName'); + $field_value = $enhancer ? $enhancer->prepareForInput($field_value) : $field_value; + $data_internal[$internal_name] = $field_value; + } + + return $data_internal; + } + + /** + * {@inheritdoc} + */ + public function normalize($entity, $format = NULL, array $context = []) { + $output = parent::normalize($entity, $format, $context); + + /** @var \Drupal\jsonapi\ResourceType\ResourceType $resource_type */ + $resource_type = $context['resource_type']; + $entity_type_id = $resource_type->getEntityTypeId(); + $bundle_id = $resource_type->getBundle(); + // The output depends on the configuration entity for caching. + $context['cacheable_metadata']->addCacheableDependency( + $this->getResourceConfig($entity_type_id, $bundle_id) + ); + $context['cacheable_metadata']->addCacheableDependency( + \Drupal::config('jsonapi_extras.settings') + ); + + return $output; + } + + /** + * Get the configuration entity based on the entity type and bundle. + * + * @param string $entity_type_id + * The entity type ID. + * @param string $bundle_id + * The bundle ID. + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The resource config entity or NULL. + */ + protected function getResourceConfig($entity_type_id, $bundle_id) { + $id = sprintf('%s--%s', $entity_type_id, $bundle_id); + // TODO: Inject this service. + $resource_config = \Drupal::entityTypeManager() + ->getStorage('jsonapi_resource_config') + ->load($id); + + return $resource_config; + } + +} diff --git a/src/Normalizer/FieldItemNormalizer.php b/src/Normalizer/FieldItemNormalizer.php index d4df1202fa8a60682a85af929ccb9607fb9492f1..b95e2981db7f4a11e1005eea26f5c0cc93405170 100644 --- a/src/Normalizer/FieldItemNormalizer.php +++ b/src/Normalizer/FieldItemNormalizer.php @@ -2,7 +2,6 @@ namespace Drupal\jsonapi_extras\Normalizer; -use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldItemInterface; use Drupal\jsonapi\Normalizer\NormalizerBase; @@ -66,103 +65,17 @@ class FieldItemNormalizer extends NormalizerBase { // First get the regular output. $normalized_output = $this->subject->normalize($object, $format, $context); // Then detect if there is any enhancer to be applied here. - /** @var \Drupal\jsonapi\ResourceType\ResourceType $resource_type */ + /** @var \Drupal\jsonapi_extras\ResourceType\ConfigurableResourceType $resource_type */ $resource_type = $context['resource_type']; - $entity_type_id = $resource_type->getEntityTypeId(); - $bundle_id = $resource_type->getBundle(); - $resource_field_info = $this->getResourceFieldInfo( - $entity_type_id, - $bundle_id, - $object->getParent()->getName() - ); - if (empty($resource_field_info['enhancer']['id'])) { + $enhancer = $resource_type->getFieldEnhancer($object->getParent()->getName()); + if (!$enhancer) { return $normalized_output; } - try { - $enhancer_info = $resource_field_info['enhancer']; - // Ensure that the settings are in a suitable format. - $settings = []; - if (!empty($enhancer_info['settings']) && is_array($enhancer_info['settings'])) { - $settings = $enhancer_info['settings']; - } - // Get the enhancer instance. - /** @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerInterface $enhancer */ - $enhancer = $this->enhancerManager->createInstance( - $enhancer_info['id'], - $settings - ); - // Apply any enhancements necessary. - $processed = $enhancer->postProcess($normalized_output->rasterizeValue()); - $normalized_output = new FieldItemNormalizerValue([$processed]); - } - catch (PluginNotFoundException $exception) { - watchdog_exception('jsonapi_extras', $exception); - } - - // TODO: This should live in an Entity level normalizer. At this point we do - // not have one, so the performance is best if we have it run multiple times - // here. However we will introduce one when altering the incoming output for - // things. - // The output depends on the configuration entity for caching. - $context['cacheable_metadata']->addCacheableDependency( - $this->getResourceConfig($entity_type_id, $bundle_id) - ); - $context['cacheable_metadata']->addCacheableDependency( - \Drupal::config('jsonapi_extras.settings') - ); + // Apply any enhancements necessary. + $processed = $enhancer->postProcess($normalized_output->rasterizeValue()); + $normalized_output = new FieldItemNormalizerValue([$processed]); return $normalized_output; } - /** - * Get information about the particular field, if it exists. - * - * @param string $entity_type_id - * The entity type ID. - * @param string $bundle_id - * The bundle ID. - * @param string $internal_field_name - * The field name to get info about. - * - * @return array|null - * The structured array with configuration or NULL if not found. - */ - protected function getResourceFieldInfo($entity_type_id, $bundle_id, $internal_field_name) { - /** @var \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $resource_config */ - $resource_config = $this->getResourceConfig($entity_type_id, $bundle_id); - if (!$resource_config) { - return NULL; - } - $resource_fields = array_filter( - $resource_config->get('resourceFields'), - function (array $resource_field) use ($internal_field_name) { - return $resource_field['fieldName'] === $internal_field_name; - } - ); - if (empty($resource_fields)) { - return NULL; - } - return reset($resource_fields); - } - - /** - * Get the configuration entity based on the entity type and bundle. - * - * @param string $entity_type_id - * The entity type ID. - * @param string $bundle_id - * The bundle ID. - * - * @return \Drupal\Core\Entity\EntityInterface|null - * The resource config entity or NULL. - */ - protected function getResourceConfig($entity_type_id, $bundle_id) { - $id = sprintf('%s--%s', $entity_type_id, $bundle_id); - $resource_config = $this->entityTypeManager - ->getStorage('jsonapi_resource_config') - ->load($id); - - return $resource_config; - } - } diff --git a/src/ResourceType/ConfigurableResourceType.php b/src/ResourceType/ConfigurableResourceType.php index 0a295f90bdff73a3a8ed062f1a1ae5ed281d8471..3280f3b5085952ef4c0ef0948791cd268788b802 100644 --- a/src/ResourceType/ConfigurableResourceType.php +++ b/src/ResourceType/ConfigurableResourceType.php @@ -2,8 +2,10 @@ namespace Drupal\jsonapi_extras\ResourceType; +use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\jsonapi\ResourceType\ResourceType; use Drupal\jsonapi_extras\Entity\JsonapiResourceConfig; +use Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager; /** * Defines a configurable resource type. @@ -17,6 +19,13 @@ class ConfigurableResourceType extends ResourceType { */ protected $jsonapiResourceConfig; + /** + * Plugin manager for enhancers. + * + * @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager + */ + protected $enhancerManager; + /** * Instantiates a ResourceType object. * @@ -28,11 +37,14 @@ class ConfigurableResourceType extends ResourceType { * The deserialization target class. * @param \Drupal\jsonapi_extras\Entity\JsonapiResourceConfig $resource_config * The configuration entity. + * @param \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager $enhancer_manager + * Plugin manager for enhancers. */ - public function __construct($entity_type_id, $bundle, $deserialization_target_class, JsonapiResourceConfig $resource_config) { + public function __construct($entity_type_id, $bundle, $deserialization_target_class, JsonapiResourceConfig $resource_config, ResourceFieldEnhancerManager $enhancer_manager) { parent::__construct($entity_type_id, $bundle, $deserialization_target_class); $this->jsonapiResourceConfig = $resource_config; + $this->enhancerManager = $enhancer_manager; if ($resource_config->get('resourceType')) { // Set the type name. @@ -68,18 +80,73 @@ class ConfigurableResourceType extends ResourceType { * {@inheritdoc} */ public function isFieldEnabled($field_name) { + $resource_field = $this->getResourceFieldConfiguration($field_name); + return $resource_field + ? empty($resource_field['disabled']) + : parent::isFieldEnabled($field_name); + } + + /** + * Get the resource field configuration. + * + * @param string $field_name + * The internal field name. + * @param string $from + * The realm of the provided field name. + * + * @return array + * The resource field definition. NULL if none can be found. + */ + public function getResourceFieldConfiguration($field_name, $from = 'fieldName') { $resource_fields = $this->jsonapiResourceConfig->get('resourceFields'); - // Find the resource field in the config entity for the given public field - // name. - $found = array_filter($resource_fields, function ($resource_field) use ($field_name) { - return !empty($resource_field['fieldName']) && - $field_name == $resource_field['fieldName']; + // Find the resource field in the config entity for the given field name. + $found = array_filter($resource_fields, function ($resource_field) use ($field_name, $from) { + return !empty($resource_field[$from]) && + $field_name == $resource_field[$from]; }); if (empty($found)) { - return parent::isFieldEnabled($field_name); + return NULL; } - $resource_field = reset($found); - return empty($resource_field['disabled']); + return reset($found); + } + + /** + * Get the field enhancer plugin. + * + * @param string $field_name + * The internal field name. + * @param string $from + * The realm of the provided field name. + * + * @return \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerInterface|null + * The enhancer plugin. NULL if not found. + */ + public function getFieldEnhancer($field_name, $from = 'fieldName') { + if (!$resource_field = $this->getResourceFieldConfiguration($field_name, $from)) { + return NULL; + } + if (empty($resource_field['enhancer']['id'])) { + return NULL; + } + try { + $enhancer_info = $resource_field['enhancer']; + // Ensure that the settings are in a suitable format. + $settings = []; + if (!empty($enhancer_info['settings']) && is_array($enhancer_info['settings'])) { + $settings = $enhancer_info['settings']; + } + // Get the enhancer instance. + /** @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerInterface $enhancer */ + $enhancer = $this->enhancerManager->createInstance( + $enhancer_info['id'], + $settings + ); + return $enhancer; + } + catch (PluginNotFoundException $exception) { + return NULL; + } + } /** @@ -96,17 +163,10 @@ class ConfigurableResourceType extends ResourceType { * The field name in the desired realm. */ private function translateFieldName($field_name, $from, $to) { - $resource_fields = $this->jsonapiResourceConfig->get('resourceFields'); - // Find the resource field in the config entity for the given field name. - $found = array_filter($resource_fields, function ($resource_field) use ($field_name, $from) { - return !empty($resource_field[$from]) && - $field_name == $resource_field[$from]; - }); - if (empty($found)) { - return $field_name; - } - $resource_field = reset($found); - return $resource_field[$to]; + $resource_field = $this->getResourceFieldConfiguration($field_name, $from); + return empty($resource_field[$to]) + ? $field_name + : $resource_field[$to]; } } diff --git a/src/ResourceType/ConfigurableResourceTypeRepository.php b/src/ResourceType/ConfigurableResourceTypeRepository.php index 66ca0a571e2c688bbee1563d5c13aa7da678d1bd..8d89549079e8e93bafcf55b48258edb4a45aeb73 100644 --- a/src/ResourceType/ConfigurableResourceTypeRepository.php +++ b/src/ResourceType/ConfigurableResourceTypeRepository.php @@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\jsonapi\ResourceType\ResourceTypeRepository; +use Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager; use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException; /** @@ -20,12 +21,20 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository { */ protected $entityRepository; + /** + * Plugin manager for enhancers. + * + * @var \Drupal\jsonapi_extras\Plugin\ResourceFieldEnhancerManager + */ + protected $enhancerManager; + /** * {@inheritdoc} */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_manager, EntityRepositoryInterface $entity_repository) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_manager, EntityRepositoryInterface $entity_repository, ResourceFieldEnhancerManager $enhancer_manager) { parent::__construct($entity_type_manager, $bundle_manager); $this->entityRepository = $entity_repository; + $this->enhancerManager = $enhancer_manager; } /** @@ -84,7 +93,8 @@ class ConfigurableResourceTypeRepository extends ResourceTypeRepository { $entity_type_id, $bundle, $this->entityTypeManager->getDefinition($entity_type_id)->getClass(), - $resource_config + $resource_config, + $this->enhancerManager ); }, $bundles); $resource_types = array_merge($resource_types, $current_types);