View source
<?php
namespace Drupal\restful\Plugin\resource\Field;
use Drupal\restful\Exception\ServerConfigurationException;
use Drupal\restful\Exception\InaccessibleRecordException;
use Drupal\restful\Exception\UnprocessableEntityException;
use Drupal\restful\Http\Request;
use Drupal\restful\Http\RequestInterface;
use Drupal\restful\Plugin\resource\DataProvider\DataProvider;
use Drupal\restful\Plugin\resource\DataInterpreter\DataInterpreterInterface;
use Drupal\restful\Plugin\resource\Field\PublicFieldInfo\PublicFieldInfoEntity;
use Drupal\restful\Plugin\resource\Field\PublicFieldInfo\PublicFieldInfoEntityInterface;
use Drupal\restful\Plugin\resource\Field\PublicFieldInfo\PublicFieldInfoInterface;
use Drupal\restful\Plugin\resource\ResourceInterface;
class ResourceFieldEntity implements ResourceFieldEntityInterface {
protected $decorated;
protected $property;
protected $subProperty;
protected $formatter;
protected $wrapperMethod = 'value';
protected $wrapperMethodOnEntity = FALSE;
protected $column;
protected $imageStyles = array();
protected $entityType;
protected $bundle;
public function __construct(array $field, RequestInterface $request) {
if ($this->decorated) {
$this
->setRequest($request);
}
if (empty($field['entityType'])) {
throw new ServerConfigurationException(sprintf('Unknown entity type for %s resource field.', __CLASS__));
}
$this
->setEntityType($field['entityType']);
$this->wrapperMethod = isset($field['wrapper_method']) ? $field['wrapper_method'] : $this->wrapperMethod;
$this->subProperty = isset($field['sub_property']) ? $field['sub_property'] : $this->subProperty;
$this->formatter = isset($field['formatter']) ? $field['formatter'] : $this->formatter;
$this->wrapperMethodOnEntity = isset($field['wrapper_method_on_entity']) ? $field['wrapper_method_on_entity'] : $this->wrapperMethodOnEntity;
$this->column = isset($field['column']) ? $field['column'] : $this->column;
$this->imageStyles = isset($field['image_styles']) ? $field['image_styles'] : $this->imageStyles;
if (!empty($field['bundle'])) {
$this
->setBundle($field['bundle']);
}
}
public static function create(array $field, RequestInterface $request = NULL, ResourceFieldInterface $decorated = NULL) {
$request = $request ?: restful()
->getRequest();
$resource_field = NULL;
$class_name = static::fieldClassName($field);
if ($class_name && class_exists($class_name) && in_array('Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityInterface', class_implements($class_name))) {
$resource_field = new $class_name($field, $request);
}
if (!$resource_field) {
$resource_field = new static($field, $request);
}
if (!$resource_field) {
throw new ServerConfigurationException('Unable to create resource field');
}
$resource_field
->decorate($decorated ? $decorated : new ResourceField($field, $request));
$resource_field->decorated
->addDefaults();
$resource_field
->addDefaults();
return $resource_field;
}
public function value(DataInterpreterInterface $interpreter) {
$value = $this->decorated
->value($interpreter);
if (isset($value)) {
return $value;
}
if (!$this
->access('view', $interpreter)) {
return NULL;
}
$property_wrapper = $this
->propertyWrapper($interpreter);
$wrapper = $interpreter
->getWrapper();
if ($property_wrapper instanceof \EntityListWrapper) {
$values = array();
foreach ($property_wrapper
->getIterator() as $item_wrapper) {
$values[] = $this
->singleValue($item_wrapper, $wrapper, $interpreter
->getAccount());
}
return $values;
}
return $this
->singleValue($property_wrapper, $wrapper, $interpreter
->getAccount());
}
public function compoundDocumentId(DataInterpreterInterface $interpreter) {
$collections = $this
->render($interpreter);
$process = function ($collection) {
if (!$collection instanceof ResourceFieldCollectionInterface) {
return $collection;
}
$id_field = $collection
->getIdField();
return $id_field
->render($collection
->getInterpreter());
};
return $this
->getCardinality() == 1 ? $process($collections) : array_map($process, array_filter($collections));
}
protected function propertyIdentifier(\EntityMetadataWrapper $property_wrapper) {
if ($property_wrapper instanceof \EntityDrupalWrapper) {
$identifier = $this
->referencedId($property_wrapper);
$resource = $this
->getResource();
if (!$resource || !$identifier || isset($resource['fullView']) && $resource['fullView'] === FALSE) {
return $identifier;
}
$instance_id = sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']);
$resource = restful()
->getResourceManager()
->getPluginCopy($instance_id, Request::create('', array(), RequestInterface::METHOD_GET));
$plugin_definition = $resource
->getPluginDefinition();
if (empty($plugin_definition['dataProvider']['idField'])) {
return $identifier;
}
try {
return $property_wrapper->{$plugin_definition['dataProvider']['idField']}
->value();
} catch (\EntityMetadataWrapperException $e) {
return $identifier;
}
}
return $this
->fieldValue($property_wrapper);
}
public function set($value, DataInterpreterInterface $interpreter) {
try {
$property_wrapper = $interpreter
->getWrapper()->{$this
->getProperty()};
$property_wrapper
->set($value);
} catch (\Exception $e) {
$this->decorated
->set($value, $interpreter);
}
}
protected function singleValue(\EntityMetadataWrapper $property_wrapper, \EntityDrupalWrapper $wrapper, $account) {
if ($resource = $this
->getResource()) {
$embedded_identifier = $this
->propertyIdentifier($property_wrapper);
if (empty($embedded_identifier) && $embedded_identifier !== 0) {
return NULL;
}
if (isset($resource['fullView']) && $resource['fullView'] === FALSE) {
return $embedded_identifier;
}
$parsed_input = array(
'fields' => implode(',', $this
->nestedDottedChildren('fields')),
'include' => implode(',', $this
->nestedDottedChildren('include')),
'filter' => $this
->nestedDottedChildren('filter'),
);
$request = Request::create('', array_filter($parsed_input), RequestInterface::METHOD_GET);
$embedded_resource = restful()
->getResourceManager()
->getPluginCopy(sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']));
$embedded_resource
->setPath($embedded_identifier);
$embedded_resource
->setRequest($request);
$embedded_resource
->setAccount($account);
$metadata = $this
->getMetadata($wrapper
->getIdentifier());
$metadata = $metadata ?: array();
$metadata[] = $this
->buildResourceMetadataItem($property_wrapper);
$this
->addMetadata($wrapper
->getIdentifier(), $metadata);
try {
$embedded_entity = $embedded_resource
->getDataProvider()
->view($embedded_identifier);
} catch (InaccessibleRecordException $e) {
return NULL;
} catch (UnprocessableEntityException $e) {
return NULL;
}
if (empty($parsed_input['filter'])) {
return $embedded_entity;
}
foreach ($parsed_input['filter'] as $filter) {
if (!empty($filter['target']) && $filter['target'] == $this
->getPublicName() && !$embedded_entity
->evalFilter($filter)) {
return NULL;
}
}
return $embedded_entity;
}
if ($this
->getFormatter()) {
$value = $this
->formatterValue($property_wrapper, $wrapper);
}
else {
$value = $this
->fieldValue($property_wrapper);
}
return $value;
}
public function access($op, DataInterpreterInterface $interpreter) {
if (!$this->decorated
->access($op, $interpreter)) {
return FALSE;
}
if (!$this
->getProperty()) {
return TRUE;
}
if (!($property_wrapper = $this
->propertyWrapper($interpreter))) {
return FALSE;
}
if ($this
->isWrapperMethodOnEntity() && $this
->getWrapperMethod() && $this
->getProperty()) {
$property_wrapper = $property_wrapper->{$this
->getProperty()};
}
$account = $interpreter
->getAccount();
if ($op == 'edit' && $property_wrapper
->type() == 'text_formatted' && $property_wrapper
->value() && $property_wrapper->format
->value()) {
$format = (object) array(
'format' => $property_wrapper->format
->value(),
);
if (!filter_access($format, $account)) {
return FALSE;
}
}
$info = $property_wrapper
->info();
if ($op == 'edit' && empty($info['setter callback'])) {
return FALSE;
}
$access = $interpreter
->getWrapper()
->value() !== FALSE && $property_wrapper
->access($op, $account);
return $access !== FALSE;
}
protected function propertyWrapper(DataInterpreterInterface $interpreter) {
$this
->setBundle($interpreter
->getWrapper()
->getBundle());
$wrapper = $interpreter
->getWrapper();
if (!$wrapper instanceof \EntityDrupalWrapper) {
throw new ServerConfigurationException('Cannot get a value without an entity metadata wrapper data source.');
}
$property = $this
->getProperty();
try {
return $property && !$this
->isWrapperMethodOnEntity() ? $wrapper->{$property} : $wrapper;
} catch (\EntityMetadataWrapperException $e) {
throw new UnprocessableEntityException(sprintf('The property %s could not be found in %s:%s.', $property, $wrapper
->type(), $wrapper
->getBundle()));
}
}
protected function fieldValue(\EntityMetadataWrapper $property_wrapper) {
if ($this
->getSubProperty() && $property_wrapper
->value()) {
$property_wrapper = $property_wrapper->{$this
->getSubProperty()};
}
return $property_wrapper
->{$this
->getWrapperMethod()}();
}
protected function formatterValue(\EntityMetadataWrapper $property_wrapper, \EntityDrupalWrapper $wrapper) {
$value = NULL;
if (!ResourceFieldEntity::propertyIsField($this
->getProperty())) {
throw new ServerConfigurationException(format_string('@property is not a configurable field, so it cannot be processed using field API formatter', array(
'@property' => $this
->getProperty(),
)));
}
$output = field_view_field($this
->getEntityType(), $wrapper
->value(), $this
->getProperty(), $this
->getFormatter());
unset($output['#theme']);
if ($property_wrapper instanceof \EntityListWrapper) {
foreach (element_children($output) as $delta) {
$value[] = drupal_render($output[$delta]);
}
}
else {
$value = drupal_render($output);
}
return $value;
}
protected function nestedDottedChildren($key) {
if ($key == 'filter') {
return $this
->nestedDottedFilters();
}
$allowed_values = array(
'include',
'fields',
);
if (!in_array($key, $allowed_values)) {
return array();
}
$input = $this
->getRequest()
->getParsedInput();
$limit_values = !empty($input[$key]) ? explode(',', $input[$key]) : array();
$limit_values = array_filter($limit_values, function ($value) {
$parts = explode('.', $value);
return $parts[0] == $this
->getPublicName() && $value != $this
->getPublicName();
});
return array_map(function ($value) {
return substr($value, strlen($this
->getPublicName()) + 1);
}, $limit_values);
}
protected function nestedDottedFilters() {
$input = $this
->getRequest()
->getParsedInput();
if (empty($input['filter'])) {
return array();
}
$output_filters = array();
$filters = $input['filter'];
foreach ($filters as $filter_public_name => $filter) {
$filter = DataProvider::processFilterInput($filter, $filter_public_name);
if (strpos($filter_public_name, $this
->getPublicName() . '.') === 0) {
$new_name = substr($filter_public_name, strlen($this
->getPublicName()) + 1);
$filter['public_field'] = $new_name;
$output_filters[$new_name] = $filter;
}
}
return $output_filters;
}
public function addMetadata($key, $value) {
$this->decorated
->addMetadata($key, $value);
}
public function getMetadata($key) {
return $this->decorated
->getMetadata($key);
}
public function getRequest() {
return $this->decorated
->getRequest();
}
public function setRequest(RequestInterface $request) {
$this->decorated
->setRequest($request);
}
public function executeProcessCallbacks($value) {
return $this->decorated
->executeProcessCallbacks($value);
}
public function render(DataInterpreterInterface $interpreter) {
return $this
->executeProcessCallbacks($this
->value($interpreter));
}
public function getDefinition() {
return $this->decorated
->getDefinition();
}
public function getPublicFieldInfo() {
return $this->decorated
->getPublicFieldInfo();
}
public function setPublicFieldInfo(PublicFieldInfoInterface $public_field_info) {
$this->decorated
->setPublicFieldInfo($public_field_info);
}
protected function resourceValue(DataInterpreterInterface $source) {
}
public function decorate(ResourceFieldInterface $decorated) {
$this->decorated = $decorated;
}
public function getSubProperty() {
return $this->subProperty;
}
public function setSubProperty($sub_property) {
$this->subProperty = $sub_property;
}
public function getFormatter() {
return $this->formatter;
}
public function setFormatter($formatter) {
$this->formatter = $formatter;
}
public function getWrapperMethod() {
return $this->wrapperMethod;
}
public function setWrapperMethod($wrapper_method) {
$this->wrapperMethod = $wrapper_method;
}
public function isWrapperMethodOnEntity() {
return $this->wrapperMethodOnEntity;
}
public function setWrapperMethodOnEntity($wrapper_method_on_entity) {
$this->wrapperMethodOnEntity = $wrapper_method_on_entity;
}
public function getColumn() {
if (isset($this->column)) {
return $this->column;
}
if ($this
->getProperty() && ($field = $this::fieldInfoField($this
->getProperty()))) {
if ($field['type'] == 'text_long') {
$this
->setColumn('value');
}
else {
$this
->setColumn(key($field['columns']));
}
}
return $this->column;
}
public function setColumn($column) {
$this->column = $column;
}
public function getImageStyles() {
return $this->imageStyles;
}
public function setImageStyles($image_styles) {
$this->imageStyles = $image_styles;
}
public function getEntityType() {
return $this->entityType;
}
public function setEntityType($entity_type) {
$this->entityType = $entity_type;
}
protected function entityTypeWrapper() {
static $entity_wrappers = array();
$key = sprintf('%s:%s', $this
->getEntityType(), $this
->getBundle());
if (isset($entity_wrappers[$key])) {
return $entity_wrappers[$key];
}
$entity_wrappers[$key] = entity_metadata_wrapper($this
->getEntityType(), NULL, array(
'bundle' => $this
->getBundle(),
));
return $entity_wrappers[$key];
}
public function getBundle() {
return $this->bundle;
}
public function setBundle($bundle) {
if (!empty($this->bundle) && $this->bundle == $bundle) {
return;
}
$this->bundle = $bundle;
if ($this
->getRequest()
->getMethod() == RequestInterface::METHOD_OPTIONS) {
$this
->populatePublicInfoField();
}
}
public function addDefaults() {
$this
->setResource($this->decorated
->getResource());
if ($this
->isWrapperMethodOnEntity() && $this
->getWrapperMethod()) {
$this
->propertyOnEntity();
}
if (($this->property = $this->decorated
->getProperty()) && ($field = $this::fieldInfoField($this->property)) && $field['type'] == 'image' && ($image_styles = $this
->getImageStyles())) {
$process_callbacks = $this
->getProcessCallbacks();
array_unshift($process_callbacks, array(
array(
$this,
'getImageUris',
),
array(
$image_styles,
),
));
$this
->setProcessCallbacks($process_callbacks);
}
}
public static function getImageUris(array $file_array, $image_styles) {
if (empty($image_styles)) {
return $file_array;
}
if (static::isArrayNumeric($file_array)) {
$output = array();
foreach ($file_array as $item) {
$output[] = static::getImageUris($item, $image_styles);
}
return $output;
}
$file_array['image_styles'] = array();
foreach ($image_styles as $style) {
$file_array['image_styles'][$style] = image_style_url($style, $file_array['uri']);
}
return $file_array;
}
public static function propertyIsField($name) {
return (bool) static::fieldInfoField($name);
}
public function preprocess($value) {
return $value;
}
public static function fieldClassName(array $field_definition) {
if (!empty($field_definition['class']) && $field_definition['class'] != '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntity') {
return $field_definition['class'];
}
if (empty($field_definition['property']) || !($field_info = static::fieldInfoField($field_definition['property']))) {
return NULL;
}
switch ($field_info['type']) {
case 'entityreference':
case 'taxonomy_term_reference':
return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityReference';
case 'text':
case 'text_long':
case 'text_with_summary':
return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityText';
case 'file':
case 'image':
if (!empty($field_definition['resource'])) {
return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldFileEntityReference';
}
return '\\Drupal\\restful\\Plugin\\resource\\Field\\ResourceFieldEntityFile';
default:
return NULL;
}
}
public function getPublicName() {
return $this->decorated
->getPublicName();
}
public function setPublicName($public_name) {
$this->decorated
->setPublicName($public_name);
}
public function getAccessCallbacks() {
return $this->decorated
->getAccessCallbacks();
}
public function setAccessCallbacks($access_callbacks) {
$this->decorated
->setAccessCallbacks($access_callbacks);
}
public function getProperty() {
return $this->property;
}
public function setProperty($property) {
$this->property = $property;
$this->decorated
->setProperty($property);
}
public function getCallback() {
return $this->decorated
->getCallback();
}
public function setCallback($callback) {
$this->decorated
->setCallback($callback);
}
public function getProcessCallbacks() {
return $this->decorated
->getProcessCallbacks();
}
public function setProcessCallbacks($process_callbacks) {
$this->decorated
->setProcessCallbacks($process_callbacks);
}
public function getResource() {
return $this->decorated
->getResource();
}
public function setResource($resource) {
$this->decorated
->setResource($resource);
}
public function getMethods() {
return $this->decorated
->getMethods();
}
public function setMethods($methods) {
$this->decorated
->setMethods($methods);
}
public function id() {
return $this->decorated
->id();
}
public function isComputed() {
return $this->decorated
->isComputed();
}
public function autoDiscovery() {
if (method_exists($this->decorated, 'autoDiscovery')) {
return $this->decorated
->autoDiscovery();
}
return ResourceFieldBase::emptyDiscoveryInfo($this
->getPublicName());
}
public function getCardinality() {
if (isset($this->cardinality)) {
return $this->cardinality;
}
$this->cardinality = 1;
if ($field_info = $this::fieldInfoField($this
->getProperty())) {
$this->cardinality = empty($field_info['cardinality']) ? $this->cardinality : $field_info['cardinality'];
}
return $this->cardinality;
}
public function setCardinality($cardinality) {
$this->cardinality = $cardinality;
}
public static function isArrayNumeric(array $input) {
return ResourceFieldBase::isArrayNumeric($input);
}
protected function buildResourceMetadataItem($wrapper) {
if ($wrapper instanceof \EntityValueWrapper) {
$wrapper = entity_metadata_wrapper($this
->getEntityType(), $wrapper
->value());
}
$id = $wrapper
->getIdentifier();
$bundle = $wrapper
->getBundle();
$resource = $this
->getResource();
return array(
'id' => $id,
'entity_type' => $wrapper
->type(),
'bundle' => $bundle,
'resource_name' => $resource['name'],
);
}
protected function referencedId($property_wrapper) {
return $property_wrapper
->getIdentifier() ?: NULL;
}
protected function propertyOnEntity() {
$property = NULL;
$wrapper_method = $this
->getWrapperMethod();
$wrapper = $this
->entityTypeWrapper();
if ($wrapper_method == 'label') {
$property = $wrapper
->entityKey('label');
}
elseif ($wrapper_method == 'getBundle') {
$property = $wrapper
->entityKey('bundle');
}
elseif ($wrapper_method == 'getIdentifier') {
$property = $wrapper
->entityKey('id');
}
if (!is_a($wrapper, '\\EntityStructureWrapper')) {
return;
}
foreach ($wrapper
->getPropertyInfo() as $wrapper_property => $property_info) {
if (!empty($property_info['schema field']) && $property_info['schema field'] == $property) {
$property = $wrapper_property;
break;
}
}
$this
->setProperty($property);
}
protected function populatePublicInfoField() {
$field_definition = $this
->getDefinition();
$discovery_info = empty($field_definition['discovery']) ? array() : $field_definition['discovery'];
$public_field_info = new PublicFieldInfoEntity($this
->getPublicName(), $this
->getProperty(), $this
->getEntityType(), $this
->getBundle(), $discovery_info);
$this
->setPublicFieldInfo($public_field_info);
if ($field_instance = field_info_instance($this
->getEntityType(), $this
->getProperty(), $this
->getBundle())) {
$public_field_info
->addSectionDefaults('info', array(
'label' => $field_instance['label'],
'description' => $field_instance['description'],
));
$field_info = $this::fieldInfoField($this
->getProperty());
$section_info = array();
$section_info['label'] = empty($field_info['label']) ? NULL : $field_info['label'];
$section_info['description'] = empty($field_info['description']) ? NULL : $field_info['description'];
$public_field_info
->addSectionDefaults('info', $section_info);
$type = $public_field_info instanceof PublicFieldInfoEntityInterface ? $public_field_info
->getFormSchemaAllowedType() : NULL;
$public_field_info
->addSectionDefaults('form_element', array(
'default_value' => isset($field_instance['default_value']) ? $field_instance['default_value'] : NULL,
'type' => $type,
));
$form_element_info = $public_field_info
->getSection('form_element');
if (!isset($form_element_info['allowed_values'])) {
$allowed_values = $public_field_info instanceof PublicFieldInfoEntityInterface ? $public_field_info
->getFormSchemaAllowedValues() : NULL;
$public_field_info
->addSectionDefaults('form_element', array(
'allowed_values' => $allowed_values,
));
}
}
else {
try {
$property_info = $this
->entityTypeWrapper()
->getPropertyInfo($this
->getProperty());
} catch (\EntityMetadataWrapperException $e) {
return;
}
if (empty($property_info)) {
return;
}
$public_field_info
->addSectionDefaults('data', array(
'type' => $property_info['type'],
'required' => empty($property_info['required']) ? FALSE : $property_info['required'],
));
$public_field_info
->addSectionDefaults('info', array(
'label' => $property_info['label'],
'description' => $property_info['description'],
));
}
}
protected static function fieldInfoField($field_name) {
return field_info_field($field_name);
}
}