bibcite-8.x-1.x-dev/modules/bibcite_entity/src/Normalizer/ReferenceNormalizerBase.php
modules/bibcite_entity/src/Normalizer/ReferenceNormalizerBase.php
<?php
namespace Drupal\bibcite_entity\Normalizer;
use Drupal\bibcite_entity\Entity\Contributor;
use Drupal\bibcite_entity\Entity\Keyword;
use Drupal\bibcite_entity\Entity\ReferenceInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Url;
use Drupal\serialization\Normalizer\EntityNormalizer;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* Base normalizer class for bibcite formats.
*/
abstract class ReferenceNormalizerBase extends EntityNormalizer {
/**
* Default reference type. Will be assigned for types without mapping.
*
* @var string
*/
const DEFAULT_REF_TYPE = 'miscellaneous';
/**
* The format that this Normalizer supports.
*
* @var string
*
* @todo Remove as it's defined in the base class.
*/
protected $format;
/**
* Format setter for DI calls.
*
* @param string|array $format
* Normalizer format.
*/
public function setFormat($format) {
$this->format = $format;
foreach ((array) $this->format as $format) {
$config_name = sprintf('bibcite_entity.mapping.%s', $format);
$config = $this->configFactory->get($config_name);
$this->fieldsMapping[$format] = $config->get('fields');
$this->typesMapping[$format] = $config->get('types');
}
}
/**
* Default publication type. Will be assigned for types without mapping.
*
* @var string
*/
public $defaultType;
/**
* Mapping between bibcite_entity and format publication types.
*
* @var array
*/
protected $typesMapping;
/**
* Mapping between bibcite_entity and format fields.
*
* @var array
*/
protected $fieldsMapping;
/**
* The interface or class that this Normalizer supports.
*
* @var array
*/
protected $supportedInterfaceOrClass = ['Drupal\bibcite_entity\Entity\ReferenceInterface'];
/**
* Configuration factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Format contributor key.
*
* @var string|null
*/
public $contributorKey = NULL;
/**
* Format keyword key.
*
* @var null|string
*/
public $keywordKey = NULL;
/**
* Format type key.
*
* Default value is 'type'.
*
* @var null|string
*/
public $typeKey = 'type';
/**
* Construct new BibliographyNormalizer object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* Config factory.
* @param \Drupal\Core\Entity\EntityTypeRepositoryInterface $entity_type_repository
* The entity type repository.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
*/
public function __construct(EntityTypeManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, EntityTypeRepositoryInterface $entity_type_repository, EntityFieldManagerInterface $entity_field_manager) {
parent::__construct($entity_manager, $entity_type_repository, $entity_field_manager);
$this->configFactory = $config_factory;
}
/**
* Get format contributor key.
*
* @return string|null
* Contributor key.
*/
protected function getContributorKey() {
return $this->contributorKey;
}
/**
* Get format keyword key.
*
* @return string|null
* Keyword key.
*/
protected function getKeywordKey() {
return $this->keywordKey;
}
/**
* Get format type key.
*
* @return string|null
* Type key.
*/
protected function getTypeKey() {
return $this->typeKey;
}
/**
* Get Drupal denormalizer.
*
* @return Drupal\serialization\Normalizer\EntityNormalizer
* Denormalized entity.
*/
public function getDrupalDenormalizer($data, $class, $format = NULL, array $context = []): mixed {
return parent::denormalize($data, $class, $format, $context);
}
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = NULL, array $context = []): mixed {
$contributor_key = $this->getContributorKey();
if (!empty($data[$contributor_key])) {
$contributors = (array) $data[$contributor_key];
unset($data[$contributor_key]);
}
$keyword_key = $this->getKeywordKey();
if (!empty($data[$keyword_key])) {
$keywords = (array) $data[$keyword_key];
unset($data[$keyword_key]);
}
$type_key = $this->getTypeKey();
if (empty($data[$type_key])) {
throw new UnexpectedValueException("Reference type is incorrect or not set.");
}
$reference_type = $data[$type_key];
$converted_type = $this->convertFormatType($reference_type, $format);
// @todo Review logic of this exception because of $this->convertFormatType() always returns value.
if (!$converted_type) {
$url = Url::fromRoute('bibcite_entity.mapping', ['bibcite_format' => $format])
->toString();
throw new UnexpectedValueException("'{$reference_type}' type is not mapped to reference type. <a href='{$url}'>Check mapping configuration</a>.");
}
unset($data[$type_key]);
$data = $this->convertKeys($data, $format);
$data['type'] = $converted_type;
/** @var \Drupal\bibcite_entity\Entity\Reference $entity */
$entity = parent::denormalize($data, $class, $format, $context);
if (!empty($contributors)) {
$author_field = $entity->get('author');
foreach ($contributors as $name) {
$author_field->appendItem(parent::denormalize(['name' => [['value' => $name]]], Contributor::class, $format, $context));
}
}
if (!empty($keywords)) {
$keyword_field = $entity->get('keywords');
foreach ($keywords as $keyword) {
$keyword_field->appendItem(parent::denormalize(['name' => [['value' => $keyword]]], Keyword::class, $format, $context));
}
}
return $entity;
}
/**
* Convert publication type to format type.
*
* @param string $type
* Bibcite entity publication type.
* @param string $format
* Serializer format.
*
* @return string
* Format publication type.
*/
protected function convertEntityType($type, $format) {
$types_mapping = array_flip(array_filter($this->typesMapping[$format]));
return isset($types_mapping[$type]) ? $types_mapping[$type] : $this->defaultType;
}
/**
* Convert format type to publication type.
*
* @param string $type
* Format publication type.
* @param string $format
* Serializer format.
*
* @return null|string
* Bibcite entity publication type.
*/
protected function convertFormatType($type, $format) {
if (isset($this->typesMapping[$format][$type])) {
return $this->typesMapping[$format][$type];
}
elseif (isset($this->typesMapping[$format][$this->defaultType])) {
return $this->typesMapping[$format][$this->defaultType];
}
return self::DEFAULT_REF_TYPE;
}
/**
* Extract fields values from reference entity.
*
* @param \Drupal\bibcite_entity\Entity\ReferenceInterface $reference
* Reference entity object.
* @param string $format
* Serializer format.
*
* @return array
* Array of entity values.
*/
protected function extractFields(ReferenceInterface $reference, $format) {
$attributes = [];
foreach ($this->fieldsMapping[$format] as $format_field => $entity_field) {
if ($entity_field && $reference->hasField($entity_field) && ($field = $reference->get($entity_field)) && !$field->isEmpty()) {
$attributes[$format_field] = $this->extractScalar($field);
}
}
return $attributes;
}
/**
* Extract keywords labels from field.
*
* @param \Drupal\Core\Field\FieldItemListInterface $field_item_list
* List of field items.
*
* @return array
* Keywords labels.
*/
protected function extractKeywords(FieldItemListInterface $field_item_list) {
$keywords = [];
foreach ($field_item_list as $field) {
$keywords[] = $field->entity ? $field->entity ->label() : NULL;
}
return $keywords;
}
/**
* Extract authors values from field.
*
* @param \Drupal\Core\Field\FieldItemListInterface $field_item_list
* List of field items.
*
* @return array
* Authors in BibTeX format.
*/
protected function extractAuthors(FieldItemListInterface $field_item_list) {
$authors = [];
foreach ($field_item_list as $field) {
$authors[] = $field->entity ? $field->entity->getName() : NULL;
}
return $authors;
}
/**
* Extract scalar value.
*
* @param \Drupal\Core\Field\FieldItemListInterface $scalar_field
* Scalar items list.
*
* @return mixed
* Scalar value.
*/
protected function extractScalar(FieldItemListInterface $scalar_field) {
return $scalar_field->value;
}
/**
* Convert format keys to Bibcite entity keys and filter non-mapped.
*
* @param array $data
* Array of decoded values.
* @param string $format
* Serializer format.
*
* @return array
* Array of decoded values with converted keys.
*/
protected function convertKeys(array $data, $format) {
$converted = [];
foreach ($data as $key => $field) {
if (!empty($this->fieldsMapping[$format][$key])) {
$converted_key = empty($this->fieldsMapping[$format][$key]) ? $key : $this->fieldsMapping[$format][$key];
$converted[$converted_key] = [$field];
}
}
return $converted;
}
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
/** @var \Drupal\bibcite_entity\Entity\ReferenceInterface $object */
$attributes = [];
$attributes[$this->typeKey] = $this->convertEntityType($object->bundle(), $format);
if ($keywords = $this->extractKeywords($object->get('keywords'))) {
$attributes[$this->keywordKey] = $keywords;
}
if ($authors = $this->extractAuthors($object->get('author'))) {
$attributes[$this->contributorKey] = $authors;
}
$attributes += $this->extractFields($object, $format);
return $attributes;
}
}
