rdf_sync-1.x-dev/src/Normalizer/RdfSyncNormalizer.php
src/Normalizer/RdfSyncNormalizer.php
<?php
declare(strict_types=1);
namespace Drupal\rdf_sync\Normalizer;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\rdf_sync\Encoder\RdfSyncEncoder;
use Drupal\rdf_sync\Event\RdfSyncNormalizeEvent;
use Drupal\rdf_sync\RdfSyncMapperInterface;
use Drupal\serialization\Normalizer\EntityNormalizer;
use EasyRdf\Literal;
use EasyRdf\RdfNamespace;
use EasyRdf\Resource;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
/**
* Normalizes data to an EasyRDF graph PHP representation.
*/
class RdfSyncNormalizer extends EntityNormalizer {
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
EntityTypeRepositoryInterface $entity_type_repository,
EntityFieldManagerInterface $entity_field_manager,
protected RdfSyncMapperInterface $mapper,
protected LanguageManagerInterface $languageManager,
protected EventDispatcherInterface $eventDispatcher,
) {
parent::__construct($entity_type_manager, $entity_type_repository, $entity_field_manager);
}
/**
* {@inheritdoc}
*/
public function normalize($object, $format = NULL, array $context = []): array {
\assert($object instanceof ContentEntityInterface);
if (!$this->mapper->isMappedEntity(entity: $object)) {
throw new NotEncodableValueException("Not acceptable format: $format");
}
$normalized = parent::normalize($object, $format, $context);
$uriFieldName = $this->mapper->getRdfUriFieldName(entity: $object);
$id = new Resource($object->get($uriFieldName)->value);
$entityType = $object->getEntityType();
unset($normalized[$entityType->getKey('id')]);
$mappings = $this->mapper->getMappings(entity: $object);
$rdfType = $this->mapper->getBundleRdfType(entity: $object);
foreach ($this->mapper->getNamespaces(entity: $object) as $prefix => $namespace) {
RdfNamespace::set($prefix, $namespace);
}
$rdf = [];
if (!$hasBundleMapping = $this->mapper->hasBundleMapping($object)) {
$rdf[$id->getUri()]['http://www.w3.org/1999/02/22-rdf-syntax-ns#type'][] = (new Resource($rdfType))->toRdfPhp();
}
foreach ($normalized as $fieldName => $items) {
foreach ($items as $delta => $item) {
foreach ($item as $columnName => $value) {
if (!$mapping = ($mappings[$fieldName][$columnName] ?? NULL)) {
// This column is not mapped.
continue;
}
$fieldDefinition = $object->getFieldDefinition($fieldName);
$itemDefinition = $fieldDefinition->getItemDefinition();
if ($fieldName === $entityType->getKey('bundle')) {
if ($hasBundleMapping) {
// This entity has bundle mapping. Taxonomy terms, for instance,
// need the bundle mapping to refer to the vocabulary resource.
$value = $rdfType;
}
}
// Resolve references to other entities. Cover also field types that
// are extending entity reference.
elseif ($columnName === 'target_id' && is_a($itemDefinition->getClass(), EntityReferenceItem::class, TRUE)) {
/** @var \Drupal\Core\Entity\ContentEntityInterface $targetEntity */
$targetEntity = $object->get($fieldName)->get($delta)->entity;
if (!$targetEntity) {
$value = NULL;
}
elseif ($this->mapper->isMappedEntity($targetEntity)) {
// If the destination is also mapped, replace the scalar ID (int,
// string) with the URI.
$targetUriFieldName = $this->mapper->getRdfUriFieldName(entity: $targetEntity);
$value = $targetEntity->get($targetUriFieldName)->value;
}
}
if ($value === NULL) {
// Don't store NULLs.
continue;
}
// phpcs:disable
// @todo implement transformations against values. A list of callables
// might be stored in $mapping. If present iterate over all and do
// cascade transformations against the value. Other option could be
// the current Inbound/OutboundValue events and subscribers.
// phpcs:enable
// Set the langcode of value.
$langCode = NULL;
if ($fieldDefinition->isTranslatable() && !$this->languageManager->isLanguageLocked($object->get($fieldName)->getLangcode())) {
$langCode = $object->get($fieldName)->getLangcode();
}
$value = $mapping->type === 'resource' ? new Resource($value) : Literal::create($value, $langCode, $mapping->type);
$predicate = new Resource($mapping->predicate);
$rdf[$id->getUri()][$predicate->getUri()][] = $value->toRdfPhp();
}
}
}
// Allow 3rd-party to alter the normalized array.
$event = new RdfSyncNormalizeEvent($rdf, $object, $context);
$this->eventDispatcher->dispatch($event);
return $event->getNormalizedArray();
}
/**
* {@inheritdoc}
*/
public function getSupportedTypes(?string $format): array {
return [ContentEntityInterface::class => TRUE];
}
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, ?string $format = NULL, array $context = []): bool {
return $this->checkFormat($format);
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, string $type, ?string $format = NULL, array $context = []): bool {
return FALSE;
}
/**
* {@inheritdoc}
*/
protected function checkFormat($format = NULL): bool {
return isset(RdfSyncEncoder::getSupportedFormats()[$format]);
}
}
