View source
<?php
namespace Drupal\restful\Plugin\formatter;
use Drupal\restful\Exception\InternalServerErrorException;
use Drupal\restful\Exception\ServerConfigurationException;
use Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface;
use Drupal\restful\Plugin\resource\Field\ResourceFieldInterface;
use Drupal\restful\Plugin\resource\Field\ResourceFieldResourceInterface;
class FormatterHalJson extends Formatter implements FormatterInterface {
const CURIE_SEPARATOR = ':';
protected $contentType = 'application/hal+json; charset=utf-8';
public function prepare(array $data) {
if (!empty($data['status']) && floor($data['status'] / 100) != 2) {
$this->contentType = 'application/problem+json; charset=utf-8';
return $data;
}
if (!($resource = $this
->getResource())) {
throw new ServerConfigurationException('Resource unavailable for HAL formatter.');
}
$is_list_request = $resource
->getRequest()
->isListRequest($resource
->getPath());
$values = $this
->extractFieldValues($data);
$values = $this
->limitFields($values);
if ($is_list_request) {
$curies_resource = $this
->withCurie($resource
->getResourceMachineName());
$output = array(
'_embedded' => array(
$curies_resource => $values,
),
);
}
else {
$output = reset($values) ?: array();
}
$data_provider = $resource
->getDataProvider();
if ($data_provider && method_exists($data_provider, 'count') && $is_list_request) {
$output['count'] = $data_provider
->count();
}
if (method_exists($resource, 'additionalHateoas')) {
$output = array_merge($output, $resource
->additionalHateoas());
}
$this
->addHateoas($output);
uksort($output, function ($a, $b) {
if ($a[0] == '_' && $b[0] == '_' || $a[0] != '_' && $b[0] != '_') {
return strcmp($a, $b);
}
return $a[0] == '_' ? 1 : -1;
});
return $output;
}
protected function addHateoas(array &$data) {
if (!($resource = $this
->getResource())) {
return;
}
$request = $resource
->getRequest();
if (!isset($data['_links'])) {
$data['_links'] = array();
}
$data['_links']['self'] = array(
'title' => 'Self',
'href' => $resource
->versionedUrl($resource
->getPath()),
);
$input = $request
->getPagerInput();
$page = $input['number'];
if ($page > 1) {
$input['number'] = $page - 1;
$data['_links']['previous'] = array(
'title' => 'Previous',
'href' => $resource
->getUrl(),
);
}
$curies_resource = $this
->withCurie($resource
->getResourceMachineName());
$listed_items = empty($data['_embedded'][$curies_resource]) ? 1 : count($data['_embedded'][$curies_resource]);
$items_per_page = $this
->calculateItemsPerPage($resource);
$previous_items = ($page - 1) * $items_per_page;
if (isset($data['count']) && $data['count'] > $listed_items + $previous_items) {
$input['number'] = $page + 1;
$data['_links']['next'] = array(
'title' => 'Next',
'href' => $resource
->getUrl(),
);
}
if (!($curie = $this
->getCurie())) {
return;
}
$curie += array(
'path' => 'doc/rels',
'template' => '/{rel}',
);
$data['_links']['curies'] = array(
'name' => $curie['name'],
'href' => url($curie['path'], array(
'absolute' => TRUE,
)) . $curie['template'],
'templated' => TRUE,
);
}
protected function extractFieldValues($data, array $parents = array(), array &$parent_hashes = array()) {
$output = array();
if ($this
->isCacheEnabled($data)) {
$parent_hashes[] = $this
->getCacheHash($data);
if ($cache = $this
->getCachedData($data)) {
return $cache->data;
}
}
foreach ($data as $public_field_name => $resource_field) {
if (!$resource_field instanceof ResourceFieldInterface) {
$parents[] = $public_field_name;
$output[$public_field_name] = static::isIterable($resource_field) ? $this
->extractFieldValues($resource_field, $parents, $parent_hashes) : $resource_field;
continue;
}
if (!$data instanceof ResourceFieldCollectionInterface) {
throw new InternalServerErrorException('Inconsistent output.');
}
$limit_fields = $data
->getLimitFields();
if ($this
->isCacheEnabled($data) && $limit_fields && !in_array($resource_field
->getPublicName(), $limit_fields)) {
continue;
}
$value = $resource_field
->render($data
->getInterpreter());
if (static::isIterable($value) && $resource_field instanceof ResourceFieldResourceInterface) {
$output += array(
'_embedded' => array(),
);
$output['_embedded'][$this
->withCurie($public_field_name)] = $this
->extractFieldValues($value, $parents, $parent_hashes);
continue;
}
$output[$public_field_name] = $value;
}
if ($this
->isCacheEnabled($data)) {
$this
->setCachedData($data, $output, $parent_hashes);
}
return $output;
}
public function render(array $structured_data) {
return drupal_json_encode($structured_data);
}
public function getContentTypeHeader() {
return $this->contentType;
}
protected function withCurie($property_name) {
if ($curie = $this
->getCurie()) {
return $property_name ? $curie['name'] . static::CURIE_SEPARATOR . $property_name : $curie['name'];
}
return $property_name;
}
protected function getCurie() {
return $this->configuration['curie'];
}
}