Commit 49b37cd4 authored by Dries's avatar Dries

Issue #1832840 by linclark: enable fieldtype-specific JSON-LD normalization.

parent 7bbf113d
<?php
/**
* @file
* Definition of Drupal\jsonld\DrupalJsonldEncoder.
*/
namespace Drupal\jsonld;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
/**
* Encodes JSON-LD data.
*
* Simply respond to JSON-LD requests using the JSON encoder.
*/
class DrupalJsonldEncoder extends JsonldEncoder implements EncoderInterface {
/**
* The format that this Encoder supports.
*
* @var string
*/
static protected $format = 'drupal_jsonld';
}
<?php
/**
* @file
* Definition of Drupal\jsonld\DrupalJsonldEntityWrapper.
*/
namespace Drupal\jsonld;
/**
* Provide an interface for DrupalJsonldNormalizer to get required properties.
*/
class DrupalJsonldEntityWrapper extends JsonldEntityWrapper {
/**
* Get properties, excluding JSON-LD specific properties.
*
* Format Entity properties for consumption by other Drupal sites. In
* Drupal's vendor specific JSON-LD, fields which correspond to primitives
* have an intermediary data structure between the entity and the value.
*/
public function getProperties() {
// Properties to skip.
$skip = array('id');
// Create language map property structure.
foreach ($this->entity->getTranslationLanguages() as $langcode => $language) {
foreach ($this->entity->getTranslation($langcode) as $name => $field) {
$definition = $this->entity->getPropertyDefinition($name);
$langKey = empty($definition['translatable']) ? 'und' : $langcode;
if (!$field->isEmpty()) {
$properties[$name][$langKey] = $field->getValue();
}
}
}
// Only return properties which are not in the $skip array.
return array_diff_key($properties, array_fill_keys($skip, ''));
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\DrupalJsonldNormalizer.
*/
namespace Drupal\jsonld;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class DrupalJsonldNormalizer extends JsonldNormalizer implements NormalizerInterface {
/**
* The format that this Normalizer supports.
*
* @var string
*/
static protected $format = 'drupal_jsonld';
/**
* The class to use for the entity wrapper object.
*
* @var string
*/
protected $entityWrapperClass = 'Drupal\jsonld\DrupalJsonldEntityWrapper';
}
......@@ -22,9 +22,41 @@ class JsonldBundle extends Bundle {
public function build(ContainerBuilder $container) {
$priority = 5;
$container->register('serializer.normalizer.jsonld', 'Drupal\jsonld\JsonldNormalizer')->addTag('normalizer', array('priority' => $priority));
$container->register('serializer.encoder.jsonld', 'Drupal\jsonld\JsonldEncoder')->addTag('encoder', array('priority' => $priority));
$container->register('serializer.normalizer.drupal_jsonld', 'Drupal\jsonld\DrupalJsonldNormalizer')->addTag('normalizer', array('priority' => $priority));
$container->register('serializer.encoder.drupal_jsonld', 'Drupal\jsonld\DrupalJsonldEncoder')->addTag('encoder', array('priority' => $priority));
// Normalizers can be specified to support a particular class and format in
// Normalizer::supportsNormalization(). Since the first matching Normalizer
// is used, Normalizers should be ordered from most specific to least
// specific.
$normalizers = array(
// Field Item.
'entity_reference' => array(
'jsonld' => 'Drupal\jsonld\JsonldEntityReferenceNormalizer',
),
'field_item' => array(
'jsonld' => 'Drupal\jsonld\JsonldFieldItemNormalizer',
),
// Entity.
'entity' => array(
'jsonld' => 'Drupal\jsonld\JsonldEntityNormalizer',
),
);
// Encoders can only specify which format they support in
// Encoder::supportsEncoding().
$encoders = array(
'jsonld' => 'Drupal\jsonld\JsonldEncoder',
);
// Add Normalizers to service container.
foreach ($normalizers as $supported_class => $formats) {
foreach ($formats as $format => $normalizer_class) {
$container->register("serializer.normalizer.{$supported_class}.{$format}", $normalizer_class)
->addTag('normalizer', array('priority' => $priority));
}
}
// Add Encoders to service container.
foreach ($encoders as $format => $encoder_class) {
$container->register("serializer.encoder.{$format}", $encoder_class)
->addTag('encoder', array('priority' => $priority));
}
}
}
......@@ -18,11 +18,11 @@
class JsonldEncoder extends JsonEncoder implements EncoderInterface {
/**
* The format that this Encoder supports.
* The formats that this Encoder supports.
*
* @var string
* @var array
*/
static protected $format = 'jsonld';
static protected $format = array('jsonld', 'drupal_jsonld');
/**
* Check whether the request is for JSON-LD.
......@@ -34,6 +34,6 @@ class JsonldEncoder extends JsonEncoder implements EncoderInterface {
* Returns TRUE if the encoder can handle the request.
*/
public function supportsEncoding($format) {
return static::$format === $format;
return in_array($format, static::$format);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\EntityNG;
use Drupal\jsonld\JsonldNormalizerBase;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class JsonldEntityNormalizer extends JsonldNormalizerBase {
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($entity, $format = NULL) {
$entityWrapper = new JsonldEntityWrapper($entity, $format, $this->serializer);
$attributes = $entityWrapper->getProperties();
$attributes = array('@id' => $entityWrapper->getId()) + $attributes;
return $attributes;
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization()
*/
public function supportsNormalization($data, $format = NULL) {
// @todo Switch to EntityInterface once all entity types are converted to
// EntityNG.
return parent::supportsNormalization($data, $format) && ($data instanceof EntityNG);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityReferenceNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\Field\Type\EntityReferenceItem;
use Drupal\jsonld\JsonldNormalizerBase;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Converts an EntityReferenceItem to a JSON-LD array structure.
*/
class JsonldEntityReferenceNormalizer extends JsonldNormalizerBase {
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($object, $format = NULL) {
// @todo If an $options parameter is added to the serialize signature, as
// requested in https://github.com/symfony/symfony/pull/4938, then instead
// of creating the array of properties, we could simply call normalize and
// pass in the referenced entity with a flag that ensures it is rendered as
// a node reference and not a node definition.
$entityWrapper = new JsonldEntityWrapper($object->entity, $format, $this->serializer);
return array(
'@id' => $entityWrapper->getId(),
);
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization()
*/
public function supportsNormalization($data, $format = NULL) {
return parent::supportsNormalization($data, $format) && ($data instanceof EntityReferenceItem);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldEntityWrapper.
......@@ -7,7 +7,7 @@
namespace Drupal\jsonld;
use Drupal\Core\Entity\EntityNG;
use Drupal\Core\Entity\Entity;
/**
* Provide an interface for JsonldNormalizer to get required properties.
......@@ -21,18 +21,41 @@ class JsonldEntityWrapper {
*/
protected $entity;
/**
* The requested format.
*
* @var string
*/
protected $format;
/**
* The serializer.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* Constructor.
*
* @param string $entity
* The Entity API entity
* @param string $format.
* The format.
* @param \Symfony\Component\Serializer\Serializer $serializer
* The serializer, provided by the SerializerAwareNormaizer.
*/
public function __construct(EntityNG $entity) {
public function __construct(Entity $entity, $format, $serializer) {
$this->entity = $entity;
$this->format = $format;
$this->serializer = $serializer;
}
/**
* Get the Entity's URI for the @id attribute.
*
* @return string
* The URI of the entity.
*/
public function getId() {
$uri_info = $this->entity->uri();
......@@ -42,11 +65,26 @@ public function getId() {
/**
* Get properties, excluding JSON-LD specific properties.
*
* Formats Entity properties in the JSON-LD array structure and removes
* unwanted values.
* @return array
* An array of properties structured as in JSON-LD.
*/
public function getProperties() {
// @todo Add property handling based on http://drupal.org/node/1813328.
return array();
// Properties to skip.
$skip = array('id');
// Create language map property structure.
foreach ($this->entity->getTranslationLanguages() as $langcode => $language) {
foreach ($this->entity->getTranslation($langcode) as $name => $field) {
$definition = $this->entity->getPropertyDefinition($name);
$langKey = empty($definition['translatable']) ? LANGUAGE_NOT_SPECIFIED : $langcode;
if (!$field->isEmpty()) {
$properties[$name][$langKey] = $this->serializer->normalize($field, $this->format);
}
}
}
// Only return properties which are not in the $skip array.
return array_diff_key($properties, array_fill_keys($skip, ''));
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldFieldItemNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\Field\FieldItemInterface;
use Drupal\jsonld\JsonldNormalizerBase;
use Symfony\Component\Serializer\Exception\RuntimeException;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class JsonldFieldItemNormalizer extends JsonldNormalizerBase {
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function normalize($object, $format = NULL) {
return $object->getPropertyValues();
}
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::supportsNormalization()
*/
public function supportsNormalization($data, $format = NULL) {
return parent::supportsNormalization($data, $format) && ($data instanceof FieldItemInterface);
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldNormalizer.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\EntityNG;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* Converts the Drupal entity object structure to JSON-LD array structure.
*/
class JsonldNormalizer implements NormalizerInterface {
/**
* The format that this Normalizer supports.
*
* @var string
*/
static protected $format = 'jsonld';
/**
* The class to use for the entity wrapper object.
*
* @var string
*/
protected $entityWrapperClass = 'Drupal\jsonld\JsonldEntityWrapper';
/**
* Normalizes an object into a set of arrays/scalars.
*
* @param object $object
* Object to normalize.
* @param string $format
* Format the normalization result will be encoded as.
*
* @return array
* An array containing the properties of the entity and JSON-LD specific
* attributes such as '@context' and '@id'.
*/
public function normalize($object, $format = NULL) {
$entityWrapper = new $this->entityWrapperClass($object);
$attributes = $entityWrapper->getProperties();
$attributes = array('@id' => $entityWrapper->getId()) + $attributes;
return $attributes;
}
/**
* Checks whether the data and format are supported by this normalizer.
*
* @param mixed $data
* Data to normalize.
* @param string $format
* Format the normalization result will be encoded as.
*
* @return bool
* Returns TRUE if the normalizer can handle the request.
*/
public function supportsNormalization($data, $format = NULL) {
// If this is an Entity object and the request is for JSON-LD.
return is_object($data) && ($data instanceof EntityNG) && static::$format === $format;
}
}
<?php
/**
* @file
* Definition of Drupal\jsonld\JsonldNormalizerBase.
*/
namespace Drupal\jsonld;
use Drupal\Core\Entity\EntityNG;
use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
/**
* Provide a base class for JSON-LD Normalizers.
*/
abstract class JsonldNormalizerBase extends SerializerAwareNormalizer implements NormalizerInterface {
/**
* The formats that this Normalizer supports.
*
* @var array
*/
static protected $format = array('jsonld', 'drupal_jsonld');
/**
* Implements \Symfony\Component\Serializer\Normalizer\NormalizerInterface::normalize()
*/
public function supportsNormalization($data, $format = NULL) {
return is_object($data) && in_array($format, static::$format);
}
}
......@@ -9,21 +9,30 @@
use Drupal\config\Tests\ConfigEntityTest;
use Drupal\Core\Language\Language;
use Drupal\jsonld\DrupalJsonldNormalizer;
use Drupal\jsonld\JsonldEntityNormalizer;
use Drupal\jsonld\JsonldEntityReferenceNormalizer;
use Drupal\jsonld\JsonldFieldItemNormalizer;
use Drupal\jsonld\Tests\JsonldNormalizerTestBase;
use Symfony\Component\Serializer\Serializer;
/**
* Test the vendor specific JSON-LD normalizer.
*/
class DrupalJsonldNormalizerTest extends JsonldNormalizerTestBase {
/**
* The format being tested.
*/
protected static $format = 'drupal_jsonld';
/**
* The normalizer to be tested.
* The Normalizers to be tested.
*/
protected $normalizer;
protected $normalizers;
public static function getInfo() {
return array(
'name' => 'Drupal JSON-LD Normalizer',
'name' => 'vnd.drupal.ld+json Normalization',
'description' => "Test Drupal's vendor specific JSON-LD normalizer.",
'group' => 'JSON-LD',
);
......@@ -35,25 +44,34 @@ public static function getInfo() {
function setUp() {
parent::setUp();
$this->normalizer = new DrupalJsonldNormalizer();
$this->normalizers = array(
'entityreference' => new JsonldEntityReferenceNormalizer(),
'field_item' => new JsonldFieldItemNormalizer(),
'entity' => new JsonldEntityNormalizer(),
);
$serializer = new Serializer($this->normalizers);
$this->normalizers['entity']->setSerializer($serializer);
}
/**
* Tests the supportsNormalization function.
*/
public function testSupportsNormalization() {
$function = 'DrupalJsonldNormalizer::supportsNormlization';
$supportedFormat = 'drupal_jsonld';
$unsupportedFormat = 'jsonld';
$format = static::$format;
$supportedEntity = entity_create('entity_test', array());
$unsupportedEntity = new ConfigEntityTest();
$field = $supportedEntity->get('uuid');
$entityreferenceField = $supportedEntity->get('user_id');
// Supported entity.
$this->assertTrue($this->normalizers['entity']->supportsNormalization($supportedEntity, static::$format), "Entity normalization is supported for $format on content entities.");
// Unsupported entity.
$this->assertFalse($this->normalizers['entity']->supportsNormalization($unsupportedEntity, static::$format), "Normalization is not supported for other entity types.");
// Supported entity, supported format.
$this->assertTrue($this->normalizer->supportsNormalization($supportedEntity, $supportedFormat), "$function returns TRUE for supported format.");
// Supported entity, unsupported format.
$this->assertFalse($this->normalizer->supportsNormalization($supportedEntity, $unsupportedFormat), "$function returns FALSE for unsupported format.");
// Unsupported entity, supported format.
$this->assertFalse($this->normalizer->supportsNormalization($unsupportedEntity, $supportedFormat), "$function returns FALSE for unsupported entity type.");
// Field item.
$this->assertTrue($this->normalizers['field_item']->supportsNormalization($field->offsetGet(0), static::$format), "Field item normalization is supported for $format.");
// Entity reference field item.
$this->assertTrue($this->normalizers['entityreference']->supportsNormalization($entityreferenceField->offsetGet(0), static::$format), "Entity reference field item normalization is supported for $format.");
}
/**
......@@ -92,14 +110,14 @@ public function testNormalize() {
'uuid' => array(
'und' => array(
array(
'value' => $entity->uuid()
'value' => $entity->uuid(),
),
),
),
'user_id' => array(
'de' => array(
array(
'value' => 1,
'@id' => url('user/' . $values['user_id'], array('absolute' => TRUE)),
),
),
),
......@@ -125,7 +143,7 @@ public function testNormalize() {
),
);
$normalized = $this->normalizer->normalize($entity);
$normalized = $this->normalizers['entity']->normalize($entity, static::$format);
// Test ordering. The @context and @id properties should always be first.
$keys = array_keys($normalized);
$this->assertEqual($keys[0], '@id', '@id and @context attributes placed correctly.');
......
<?php
/**
* @file
* Definition of Drupal\jsonld\Tests\JsonldNormalizerTest.
*/
namespace Drupal\jsonld\Tests;
use Drupal\config\Tests\ConfigEntityTest;
use Drupal\jsonld\JsonldNormalizer;
use Drupal\jsonld\Tests\JsonldNormalizerTestBase;
/**
* Test the default JSON-LD normalizer.
*/
class JsonldNormalizerTest extends JsonldNormalizerTestBase {
/**
* The normalizer to be tested.
*/
protected $normalizer;
public static function getInfo() {
return array(
'name' => 'JSON-LD Normalizer',
'description' => "Test the JSON-LD normalizer.",
'group' => 'JSON-LD',
);
}
/**
* Add the normalizer to be tested.
*/
function setUp() {
parent::setUp();
$this->normalizer = new JsonldNormalizer();
}
/**
* Tests the supportsNormalization function.
*/
public function testSupportsNormalization() {
$function = 'JsonldNormalizer::supportsNormlization';
$supportedFormat = 'jsonld';
$unsupportedFormat = 'drupal_jsonld';
$supportedEntity = entity_create('entity_test', array());
$unsupportedEntity = new ConfigEntityTest();
// Supported entity, supported format.
$this->assertTrue($this->normalizer->supportsNormalization($supportedEntity, $supportedFormat), "$function returns TRUE for supported format.");
// Supported entity, unsupported format.
$this->assertFalse($this->normalizer->supportsNormalization($supportedEntity, $unsupportedFormat), "$function returns FALSE for unsupported format.");
// Unsupported entity, supported format.
$this->assertFalse($this->normalizer->supportsNormalization($unsupportedEntity, $supportedFormat), "$function returns FALSE for unsupported entity type.");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment