View source
<?php
namespace Drupal\restful\Plugin\resource\DataProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Drupal\restful\Exception\BadRequestException;
use Drupal\restful\Exception\ServerConfigurationException;
use Drupal\restful\Exception\ServiceUnavailableException;
use Drupal\restful\Http\RequestInterface;
use Drupal\restful\Plugin\resource\DataInterpreter\ArrayWrapper;
use Drupal\restful\Plugin\resource\DataInterpreter\DataInterpreterArray;
use Drupal\restful\Plugin\resource\Decorators\CacheDecoratedResource;
use Drupal\restful\Plugin\resource\Field\ResourceFieldCollection;
use Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface;
use Drupal\restful\Plugin\resource\Field\ResourceFieldDbColumnInterface;
use Drupal\restful\Plugin\resource\ResourceInterface;
class DataProviderDbQuery extends DataProvider implements DataProviderDbQueryInterface {
protected $tableName;
protected $idColumn;
const COLUMN_IDS_SEPARATOR = '::';
protected $primary;
public function __construct(RequestInterface $request, ResourceFieldCollectionInterface $field_definitions, $account, $plugin_id, $resource_path = NULL, array $options = array(), $langcode = NULL) {
parent::__construct($request, $field_definitions, $account, $plugin_id, $resource_path, $options, $langcode);
$required_keys = array(
'tableName',
'idColumn',
);
$required_callback = function ($required_key) {
if (!$this->options[$required_key]) {
throw new ServiceUnavailableException(sprintf('%s is missing "%s" property in the "dataProvider" key of the $plugin', get_class($this), $required_key));
}
};
array_walk($required_keys, $required_callback);
$this->tableName = $this->options['tableName'];
$this->idColumn = $this->options['idColumn'];
$this->primary = empty($this->options['primary']) ? NULL : ($this->primary = $this->options['primary']);
}
public function getTableName() {
return $this->tableName;
}
public function setTableName($table_name) {
$this->tableName = $table_name;
}
public function getPrimary() {
return $this->primary;
}
public function setPrimary($primary) {
$this->primary = $primary;
}
public function getCacheFragments($identifier) {
if (is_array($identifier)) {
$identifier = implode(ResourceInterface::IDS_SEPARATOR, $identifier);
}
$fragments = new ArrayCollection(array(
'resource' => CacheDecoratedResource::serializeKeyValue($this->pluginId, $this
->canonicalPath($identifier)),
'table_name' => $this
->getTableName(),
'column' => implode(',', $this
->getIdColumn()),
));
$options = $this
->getOptions();
switch ($options['renderCache']['granularity']) {
case DRUPAL_CACHE_PER_USER:
if ($uid = $this
->getAccount()->uid) {
$fragments
->set('user_id', (int) $uid);
}
break;
case DRUPAL_CACHE_PER_ROLE:
$fragments
->set('user_role', implode(',', $this
->getAccount()->roles));
break;
}
return $fragments;
}
public function count() {
return intval($this
->getQueryForList()
->countQuery()
->execute()
->fetchField());
}
public function isPrimaryField($field_name) {
return $this->primary == $field_name;
}
protected function getIdColumn() {
return is_array($this->idColumn) ? $this->idColumn : array(
$this->idColumn,
);
}
public function create($object) {
$save = FALSE;
$original_object = $object;
$id_columns = $this
->getIdColumn();
$record = array();
foreach ($this->fieldDefinitions as $public_field_name => $resource_field) {
if (!$this
->methodAccess($resource_field)) {
unset($original_object[$public_field_name]);
continue;
}
$property_name = $resource_field
->getProperty();
if ($this
->isPrimaryField($property_name)) {
unset($original_object[$public_field_name]);
continue;
}
if (isset($object[$public_field_name])) {
$record[$property_name] = $object[$public_field_name];
}
unset($original_object[$public_field_name]);
$save = TRUE;
}
if (!$save) {
throw new BadRequestException('No values were sent with the request.');
}
if (!empty($original_object)) {
$error_message = format_plural(count($original_object), 'Property @names is invalid.', 'Property @names are invalid.', array(
'@names' => implode(', ', array_keys($original_object)),
));
throw new BadRequestException($error_message);
}
if (drupal_write_record($this
->getTableName(), $record)) {
$id_values = array();
foreach ($id_columns as $id_column) {
$id_values[$id_column] = $record[$id_column];
}
$new_id = implode(self::COLUMN_IDS_SEPARATOR, $id_values);
return array(
$this
->view($new_id),
);
}
return NULL;
}
public function view($identifier) {
$query = $this
->getQuery();
foreach ($this
->getIdColumn() as $index => $column) {
$identifier = is_array($identifier) ? $identifier : array(
$identifier,
);
$query
->condition($this
->getTableName() . '.' . $column, current($this
->getColumnFromIds($identifier, $index)));
}
$this
->addExtraInfoToQuery($query);
$result = $query
->range(0, 1)
->execute()
->fetch(\PDO::FETCH_OBJ);
return $this
->mapDbRowToPublicFields($result);
}
protected function mapDbRowToPublicFields($row) {
$resource_field_collection = $this
->initResourceFieldCollection($row);
foreach ($this->fieldDefinitions as $public_field_name => $resource_field) {
$value = NULL;
if (!$this
->methodAccess($resource_field)) {
continue;
}
$resource_field_collection
->set($resource_field
->id(), $resource_field);
}
return $resource_field_collection;
}
protected function addExtraInfoToQuery($query) {
$query
->addTag('restful');
$query
->addMetaData('account', $this
->getAccount());
$query
->addMetaData('restful_handler', $this);
}
public function viewMultiple(array $identifiers) {
$query = $this
->getQueryForList();
if (empty($identifiers)) {
return array();
}
foreach ($this
->getIdColumn() as $index => $column) {
$query
->condition($this
->getTableName() . '.' . $column, $this
->getColumnFromIds($identifiers, $index), 'IN');
}
$results = $query
->execute();
$return = array();
foreach ($results as $result) {
$return[] = $this
->mapDbRowToPublicFields($result);
}
return $return;
}
protected function getQueryForList() {
$query = $this
->getQuery();
$this
->queryForListSort($query);
$this
->queryForListFilter($query);
$this
->queryForListPagination($query);
$this
->addExtraInfoToQuery($query);
return $query;
}
public function update($identifier, $object, $replace = FALSE) {
$save = FALSE;
$original_object = $object;
$id_columns = $this
->getIdColumn();
$record = array();
foreach ($this->fieldDefinitions as $public_field_name => $resource_field) {
if (!$this
->methodAccess($resource_field)) {
unset($original_object[$public_field_name]);
continue;
}
$property = $resource_field
->getProperty();
if ($this
->isPrimaryField($property)) {
continue;
}
if (isset($object[$public_field_name])) {
$record[$property] = $object[$public_field_name];
}
elseif ($replace) {
$record[$property] = NULL;
}
unset($original_object[$public_field_name]);
$save = TRUE;
}
if (!$save) {
throw new BadRequestException('No values were sent with the request.');
}
if (!empty($original_object)) {
$error_message = format_plural(count($original_object), 'Property @names is invalid.', 'Property @names are invalid.', array(
'@names' => implode(', ', array_keys($original_object)),
));
throw new BadRequestException($error_message);
}
foreach ($this
->getIdColumn() as $index => $column) {
$record[$column] = current($this
->getColumnFromIds(array(
$identifier,
), $index));
}
if (!drupal_write_record($this
->getTableName(), $record, $id_columns)) {
throw new ServiceUnavailableException('Record could not be updated to the database.');
}
return array(
$this
->view($identifier),
);
}
public function remove($identifier) {
$this
->setHttpHeader('Status', 204);
$query = db_delete($this
->getTableName());
foreach ($this
->getIdColumn() as $index => $column) {
$query
->condition($column, current($this
->getColumnFromIds(array(
$identifier,
), $index)));
}
$query
->execute();
}
public function getIndexIds() {
$results = $this
->getQueryForList()
->execute();
$ids = array();
foreach ($results as $result) {
$ids[] = array_map(function ($id_column) use ($result) {
return $result->{$id_column};
}, $this
->getIdColumn());
}
return $ids;
}
public function index() {
$results = $this
->getQueryForList()
->execute();
$return = array();
foreach ($results as $result) {
$return[] = $this
->mapDbRowToPublicFields($result);
}
return $return;
}
protected function defaultSortInfo() {
$sorts = array();
foreach ($this
->getIdColumn() as $column) {
if (!$this->fieldDefinitions
->get($column)) {
$sorts[$column] = 'ASC';
break;
}
}
return $sorts;
}
protected function queryForListSort(\SelectQuery $query) {
$sorts = $this
->parseRequestForListSort();
$sorts = $sorts ? $sorts : $this
->defaultSortInfo();
foreach ($sorts as $sort => $direction) {
if ($sort_field = $this->fieldDefinitions
->get($sort)) {
$query
->orderBy($sort_field
->getColumnForQuery(), $direction);
}
}
}
protected function queryForListFilter(\SelectQuery $query) {
foreach ($this
->parseRequestForListFilter() as $filter) {
if (!($filter_field = $this->fieldDefinitions
->get($filter['public_field']))) {
continue;
}
$column_name = $filter_field
->getColumnForQuery();
if (in_array(strtoupper($filter['operator'][0]), array(
'IN',
'NOT IN',
'BETWEEN',
))) {
$query
->condition($column_name, $filter['value'], $filter['operator'][0]);
continue;
}
$condition = db_condition($filter['conjunction']);
for ($index = 0; $index < count($filter['value']); $index++) {
$condition
->condition($column_name, $filter['value'][$index], $filter['operator'][$index]);
}
$query
->condition($condition);
}
}
protected function queryForListPagination(\SelectQuery $query) {
list($range, $offset) = $this
->parseRequestForListPagination();
$query
->range($range, $offset);
}
protected function getQuery() {
$table = $this
->getTableName();
return db_select($table)
->fields($table);
}
protected function getColumnFromIds(array $identifiers, $column = 0) {
$get_part = function ($identifier) use ($column) {
$parts = explode(static::COLUMN_IDS_SEPARATOR, $identifier);
if (!isset($parts[$column])) {
throw new ServerConfigurationException('Invalid ID provided.');
}
return $parts[$column];
};
return array_map($get_part, $identifiers);
}
protected function initDataInterpreter($identifier) {
return new DataInterpreterArray($this
->getAccount(), new ArrayWrapper((array) $identifier));
}
}