RestfulDataProviderEFQ.php in RESTful 7
Contains \RestfulDataProviderEFQ
File
plugins/restful/RestfulDataProviderEFQ.phpView source
<?php
/**
* @file
* Contains \RestfulDataProviderEFQ
*/
abstract class RestfulDataProviderEFQ extends \RestfulBase implements \RestfulDataProviderEFQInterface, \RestfulDataProviderInterface {
/**
* The entity type.
*
* @var string
*/
protected $entityType;
/**
* The bundle.
*
* @var string
*/
protected $bundle;
/**
* The bundle.
*
* @var string
*/
protected $EFQClass = '\\EntityFieldQuery';
/**
* Getter for $bundle.
*
* @return string
*/
public function getBundle() {
return $this->bundle;
}
/**
* Getter for $entityType.
*
* @return string
*/
public function getEntityType() {
return $this->entityType;
}
/**
* Get the entity info for the current entity the endpoint handling.
*
* @param null $type
* The entity type. Optional.
* @return array
* The entity info.
*/
public function getEntityInfo($type = NULL) {
return entity_get_info($type ? $type : $this
->getEntityType());
}
/**
* Constructs a RestfulDataProviderEFQ object.
*
* @param array $plugin
* Plugin definition.
* @param RestfulAuthenticationManager $auth_manager
* (optional) Injected authentication manager.
* @param DrupalCacheInterface $cache_controller
* (optional) Injected cache backend.
* @param string $language
* (optional) The language to return items in.
*
* @throws RestfulServerConfigurationException
*/
public function __construct(array $plugin, \RestfulAuthenticationManager $auth_manager = NULL, \DrupalCacheInterface $cache_controller = NULL, $language = NULL) {
parent::__construct($plugin, $auth_manager, $cache_controller, $language);
$this->entityType = $plugin['entity_type'];
$this->bundle = $plugin['bundle'];
// Allow providing an alternative to \EntityFieldQuery.
$data_provider_options = $this
->getPluginKey('data_provider_options');
if (!empty($data_provider_options['efq_class'])) {
if (!is_subclass_of($data_provider_options['efq_class'], '\\EntityFieldQuery')) {
throw new \RestfulServerConfigurationException(format_string('The provided class @class does not extend from \\EntityFieldQuery.', array(
'@class' => $data_provider_options['efq_class'],
)));
}
$this->EFQClass = $data_provider_options['efq_class'];
}
}
/**
* Defines default sort fields if none are provided via the request URL.
*
* @return array
* Array keyed by the public field name, and the order ('ASC' or 'DESC') as value.
*/
public function defaultSortInfo() {
return array(
'id' => 'ASC',
);
}
/**
* {@inheritdoc}
*/
public function getQueryForList() {
$entity_type = $this
->getEntityType();
$query = $this
->getEntityFieldQuery();
if ($path = $this
->getPath()) {
$ids = explode(',', $path);
if (!empty($ids)) {
$query
->entityCondition('entity_id', $ids, 'IN');
}
}
$this
->queryForListSort($query);
$this
->queryForListFilter($query);
$this
->queryForListPagination($query);
$this
->addExtraInfoToQuery($query);
return $query;
}
/**
* Sort the query for list.
*
* @param \EntityFieldQuery $query
* The query object.
*
* @throws \RestfulBadRequestException
*
* @see \RestfulEntityBase::getQueryForList
*/
protected function queryForListSort(\EntityFieldQuery $query) {
$public_fields = $this
->getPublicFields();
// Get the sorting options from the request object.
$sorts = $this
->parseRequestForListSort();
$sorts = $sorts ? $sorts : $this
->defaultSortInfo();
foreach ($sorts as $public_field_name => $direction) {
// Determine if sorting is by field or property.
if (!($property_name = $public_fields[$public_field_name]['property'])) {
throw new \RestfulBadRequestException('The current sort selection does not map to any entity property or Field API field.');
}
if (field_info_field($property_name)) {
$query
->fieldOrderBy($public_fields[$public_field_name]['property'], $public_fields[$public_field_name]['column'], $direction);
}
else {
$column = $this
->getColumnFromProperty($property_name);
$query
->propertyOrderBy($column, $direction);
}
}
}
/**
* Filter the query for list.
*
* @param \EntityFieldQuery $query
* The query object.
*
* @throws \RestfulBadRequestException
*
* @see \RestfulEntityBase::getQueryForList
*/
protected function queryForListFilter(\EntityFieldQuery $query) {
$public_fields = $this
->getPublicFields();
foreach ($this
->parseRequestForListFilter() as $filter) {
// Determine if filtering is by field or property.
if (!($property_name = $public_fields[$filter['public_field']]['property'])) {
throw new \RestfulBadRequestException('The current filter selection does not map to any entity property or Field API field.');
}
if (field_info_field($property_name)) {
if (in_array(strtoupper($filter['operator'][0]), array(
'IN',
'NOT IN',
'BETWEEN',
))) {
if (is_array($filter['value']) && empty($filter['value'])) {
if (strtoupper($filter['operator'][0]) == 'NOT IN') {
// Skip filtering by an empty value when operator is 'NOT IN',
// since it throws an SQL error.
continue;
}
// Since Drupal doesn't know how to handle an empty array within a
// condition we add the `NULL` as an element to the array.
$filter['value'] = array(
NULL,
);
}
$query
->fieldCondition($public_fields[$filter['public_field']]['property'], $public_fields[$filter['public_field']]['column'], $filter['value'], $filter['operator'][0]);
continue;
}
for ($index = 0; $index < count($filter['value']); $index++) {
$query
->fieldCondition($public_fields[$filter['public_field']]['property'], $public_fields[$filter['public_field']]['column'], $filter['value'][$index], $filter['operator'][$index]);
}
}
else {
$column = $this
->getColumnFromProperty($property_name);
if (in_array(strtoupper($filter['operator'][0]), array(
'IN',
'NOT IN',
'BETWEEN',
))) {
if (is_array($filter['value']) && empty($filter['value'])) {
if (strtoupper($filter['operator'][0]) == 'NOT IN') {
// Skip filtering by an empty value when operator is 'NOT IN',
// since it throws an SQL error.
continue;
}
// Since Drupal doesn't know how to handle an empty array within a
// condition we add the `NULL` as an element to the array.
$filter['value'] = array(
NULL,
);
}
$query
->propertyCondition($column, $filter['value'], $filter['operator'][0]);
continue;
}
for ($index = 0; $index < count($filter['value']); $index++) {
$query
->propertyCondition($column, $filter['value'][$index], $filter['operator'][$index]);
}
}
}
}
/**
* Get the DB column name from a property.
*
* The "property" defined in the public field is actually the property
* of the entity metadata wrapper. Sometimes that property can be a
* different name than the column in the DB. For example, for nodes the
* "uid" property is mapped in entity metadata wrapper as "author", so
* we make sure to get the real column name.
*
* @param string $property_name
* The property name.
*
* @return string
* The column name.
*/
protected function getColumnFromProperty($property_name) {
$property_info = entity_get_property_info($this
->getEntityType());
return $property_info['properties'][$property_name]['schema field'];
}
/**
* Overrides \RestfulBase::isValidOperatorsForFilter().
*/
protected static function isValidOperatorsForFilter(array $operators) {
$allowed_operators = array(
'=',
'>',
'<',
'>=',
'<=',
'<>',
'!=',
'BETWEEN',
'CONTAINS',
'IN',
'LIKE',
'NOT IN',
'STARTS_WITH',
);
foreach ($operators as $operator) {
if (!in_array($operator, $allowed_operators)) {
throw new \RestfulBadRequestException(format_string('Operator "@operator" is not allowed for filtering on this resource. Allowed operators are: !allowed', array(
'@operator' => $operator,
'!allowed' => implode(', ', $allowed_operators),
)));
}
}
}
/**
* Overrides \RestfulBase::isValidConjuctionForFilter().
*/
protected static function isValidConjunctionForFilter($conjunction) {
$allowed_conjunctions = array(
'AND',
);
if (!in_array(strtoupper($conjunction), $allowed_conjunctions)) {
throw new \RestfulBadRequestException(format_string('Conjunction "@conjunction" is not allowed for filtering on this resource. Allowed conjunctions are: !allowed', array(
'@conjunction' => $conjunction,
'!allowed' => implode(', ', $allowed_conjunctions),
)));
}
}
/**
* Set correct page (i.e. range) for the query for list.
*
* Determine the page that should be seen. Page 1, is actually offset 0 in the
* query range.
*
* @param \EntityFieldQuery $query
* The query object.
*
* @throws \RestfulBadRequestException
*
* @see \RestfulEntityBase::getQueryForList
*/
protected function queryForListPagination(\EntityFieldQuery $query) {
list($offset, $range) = $this
->parseRequestForListPagination();
$query
->range($offset, $range);
}
/**
* {@inheritdoc}
*/
public function getQueryCount() {
$query = $this
->getEntityFieldQuery();
if ($path = $this
->getPath()) {
$ids = explode(',', $path);
$query
->entityCondition('entity_id', $ids, 'IN');
}
$this
->queryForListFilter($query);
$this
->addExtraInfoToQuery($query);
$query
->addTag('restful_count');
return $query
->count();
}
/**
* {@inheritdoc}
*/
public function getTotalCount() {
return intval($this
->getQueryCount()
->execute());
}
/**
* Adds query tags and metadata to the EntityFieldQuery.
*
* @param \EntityFieldQuery $query
* The query to enhance.
*/
protected function addExtraInfoToQuery($query) {
parent::addExtraInfoToQuery($query);
$entity_type = $this
->getEntityType();
// The only time you need to add the access tags to a EFQ is when you don't
// have fieldConditions.
if (empty($query->fieldConditions)) {
// Add a generic entity access tag to the query.
$query
->addTag($entity_type . '_access');
}
$query
->addMetaData('restful_handler', $this);
}
/**
* {@inheritdoc}
*/
public function index() {
// Defer the actual implementation to \RestfulEntityBase.
return $this
->getList();
}
/**
* {@inheritdoc}
*/
public function viewMultiple(array $ids) {
// Defer the actual implementation to \RestfulEntityBase.
return $this
->viewEntities(implode(',', $ids));
}
/**
* {@inheritdoc}
*/
public function view($id) {
// Defer the actual implementation to \RestfulEntityBase.
return $this
->viewEntity($id);
}
/**
* {@inheritdoc}
*/
public function update($id, $full_replace = FALSE) {
// Defer the actual implementation to \RestfulEntityBase.
return $this
->updateEntity($id, $full_replace);
}
/**
* {@inheritdoc}
*/
public function create() {
// Defer the actual implementation to \RestfulEntityBase.
return $this
->createEntity();
}
/**
* {@inheritdoc}
*/
public function remove($id) {
// Defer the actual implementation to \RestfulEntityBase.
$this
->deleteEntity($id);
}
/**
* Get a list of entities.
*
* @return array
* Array of entities, as passed to RestfulEntityBase::viewEntity().
*
* @throws RestfulBadRequestException
*/
public abstract function getList();
/**
* View an entity.
*
* @param $id
* The ID to load the entity.
*
* @return array
* Array with the public fields populated.
*
* @throws Exception
*/
public abstract function viewEntity($id);
/**
* Get a list of entities based on a list of IDs.
*
* @param string $ids_string
* Coma separated list of ids.
*
* @return array
* Array of entities, as passed to RestfulEntityBase::viewEntity().
*
* @throws RestfulBadRequestException
*/
public abstract function viewEntities($ids_string);
/**
* Create a new entity.
*
* @return array
* Array with the output of the new entity, passed to
* RestfulEntityInterface::viewEntity().
*
* @throws RestfulForbiddenException
*/
public abstract function createEntity();
/**
* Update an entity.
*
* @param $id
* The ID to load the entity.
* @param bool $null_missing_fields
* Determine if properties that are missing form the request array should
* be treated as NULL, or should be skipped. Defaults to FALSE, which will
* skip missing the fields to NULL.
*
* @return array
* Array with the output of the new entity, passed to
* RestfulEntityInterface::viewEntity().
*/
protected abstract function updateEntity($id, $null_missing_fields = FALSE);
/**
* Delete an entity using DELETE.
*
* No result is returned, just the HTTP header is set to 204.
*
* @param $id
* The ID to load the entity.
*/
public abstract function deleteEntity($id);
/**
* Initialize an EntityFieldQuery (or extending class).
*
* @return \EntityFieldQuery
* The initialized query with the basics filled in.
*/
protected function getEntityFieldQuery() {
$query = $this
->EFQObject();
$entity_type = $this
->getEntityType();
$query
->entityCondition('entity_type', $entity_type);
$entity_info = $this
->getEntityInfo();
if ($this
->getBundle() && $entity_info['entity keys']['bundle']) {
$query
->entityCondition('bundle', $this
->getBundle());
}
return $query;
}
/**
* Gets a EFQ object.
*
* @return \EntityFieldQuery
* The object that inherits from \EntityFieldQuery.
*/
protected function EFQObject() {
$efq_class = $this->EFQClass;
return new $efq_class();
}
}
Classes
Name | Description |
---|---|
RestfulDataProviderEFQ | @file Contains \RestfulDataProviderEFQ |