external_entities-8.x-2.x-dev/modules/xntt_views/xntt_views.views.inc
modules/xntt_views/xntt_views.views.inc
<?php
/**
* @file
* Views hook implementations for External Entities.
*
* Most of this code is inspired or borrowed from Search API module.
*/
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
use Drupal\Core\Utility\Error;
/**
* Implements hook_views_data().
*/
function xntt_views_views_data() {
$data = [];
$xntt_type_storage = \Drupal::entityTypeManager()->getStorage('external_entity_type');
$field_manager = \Drupal::service('entity_field.manager');
$entity_types = $xntt_type_storage->getQuery()->accessCheck(TRUE)->execute();
foreach ($entity_types as $entity_type_mname) {
$entity_type = $xntt_type_storage->load($entity_type_mname);
$view_id = 'xntt_views_' . $entity_type_mname;
// Base data.
$group_name = $entity_type->getLabel();
$entity_type_name = $entity_type->getLabel();
$data[$view_id] = [
'table' => [
'group' => $group_name,
'provider' => 'xntt_views',
'base' => [
'title' => $entity_type_name,
'help' => t(
'@xntttype external entities views mapping.',
['@xnttype' => $entity_type_name]
),
'query_id' => 'xntt_views',
'xntt' => $entity_type_mname,
],
'entity revision' => FALSE,
'entity type' => $entity_type_mname,
],
'rendered_entity' => [
'field' => [
'title' => t('Rendered entity'),
'help' => t('Renders an entity in a view mode.'),
'id' => 'rendered_entity',
],
],
];
// Fields.
$fields = $field_manager->getFieldDefinitions($entity_type_mname, $entity_type_mname);
foreach ($fields as $field_id => $field_definition) {
// @todo Needs refactoring to generate things from distinct functions.
$field_view_def = _xntt_views_get_view_field($field_definition);
$field_filter = _xntt_views_get_view_field($field_definition);
$data[$view_id][$field_id] = [
'title' => $field_definition->getLabel(),
'help' => $field_definition->getDescription(),
'field' => ['id' => 'xnttfield'],
'argument' => $field_view_def,
'filter' => $field_filter,
'sort' => ['id' => 'standard'],
'entity field' => $field_id,
];
}
}
return $data;
}
/**
* Returns the Views handlers to use for a given field.
*
* @param \Drupal\Core\Field\FieldInterface $field
* The field to add to the definition.
*
* @return array
* The Views definition to add for the given field.
*/
function _xntt_views_get_view_field(FieldDefinitionInterface $field) {
// @todo Set field type according to field.
// @see /core/modules/views/src/Plugin/views/field
// And use code below as basis (_xntt_views_views_handler_mapping()).
$type = $field->getType();
$view_field = [];
switch ($type) {
case 'boolean':
$view_field = ['id' => 'boolean'];
break;
case 'changed':
$view_field = ['id' => 'date'];
break;
case 'created':
$view_field = ['id' => 'date'];
break;
case 'decimal':
$view_field = ['id' => 'numeric'];
break;
case 'email':
$view_field = ['id' => 'string'];
break;
case 'entity_reference':
$view_field = [
'id' => 'entity_label',
];
if ($field instanceof BaseFieldDefinition) {
$view_field['entity type field'] = $field->getName();
}
else {
$view_field['entity type field'] = $field->get('field_name');
}
break;
case 'float':
$view_field = ['id' => 'numeric'];
break;
case 'integer':
$view_field = ['id' => 'numeric'];
break;
case 'language':
$view_field = ['id' => 'language'];
break;
case 'map':
$view_field = ['id' => 'string'];
break;
case 'password':
$view_field = ['id' => 'string'];
break;
case 'string':
$view_field = ['id' => 'string'];
break;
case 'string_long':
$view_field = ['id' => 'string'];
break;
case 'timestamp':
$view_field = ['id' => 'date'];
break;
case 'uri':
$view_field = ['id' => 'url'];
break;
case 'uuid':
$view_field = ['id' => 'string'];
break;
default:
$view_field = ['id' => 'string'];
break;
}
return $view_field;
}
/**
* Finds an unused field alias for a field in a Views table definition.
*
* @param string $field_id
* The original ID of the field.
* @param array $table
* The Views table definition.
*
* @return string
* The field alias to use.
*/
function _xntt_views_views_find_field_alias($field_id, array &$table) {
$base = $field_alias = preg_replace('/[^a-zA-Z0-9]+/S', '_', $field_id);
$i = 0;
while (isset($table[$field_alias])) {
$field_alias = $base . '_' . ++$i;
}
return $field_alias;
}
/**
* Returns the Views handlers to use for a given field.
*
* @param \Drupal\Core\Field\FieldInterface $field
* The field to add to the definition.
*
* @return array
* The Views definition to add for the given field.
*/
function _xntt_views_views_get_handlers(FieldDefinitionInterface $field) {
$mapping = _xntt_views_views_handler_mapping();
try {
$types = [];
$definition = $field->getDataDefinition();
$entity_type_id = $definition->getSetting('target_type');
if ($entity_type_id) {
$entity_type = \Drupal::entityTypeManager()
->getDefinition($entity_type_id);
$bundle_of = $entity_type->getBundleOf();
if ($bundle_of) {
$types[] = "bundle_of:$bundle_of";
$types[] = ['bundle_of', $bundle_of];
}
$types[] = "entity:$entity_type_id";
$types[] = ['entity', $entity_type_id];
}
// Special treatment for languages (as we have no specific data type for
// those).
if ($definition->getSetting('views_type') === 'language') {
$types[] = 'language';
}
if ($definition->getSetting('allowed_values')) {
$types[] = 'options';
}
$types[] = $field->getDataType();
$data_type = \Drupal::service('plugin.manager.xntt_views.data_type')->createInstance($field->getDataType());
if (!$data_type->isDefault()) {
$types[] = $data_type->getFallbackType();
}
foreach ($types as $type) {
$sub_type = NULL;
if (is_array($type)) {
[$type, $sub_type] = $type;
}
if (isset($mapping[$type])) {
_xntt_views_views_handler_adjustments($type, $field, $mapping[$type], $sub_type);
return $mapping[$type];
}
}
}
// @todo Replace with multi-catch once we depend on PHP 7.1+.
catch (PluginNotFoundException $e) {
$vars['%index'] = $field->getIndex()->label();
$vars['%field'] = $field->getPrefixedLabel();
Error::logException(\Drupal::logger('xntt_views'), $e, '%type while adding Views handlers for field %field on index %index: @message in %function (line %line of %file).', $vars);
}
return [];
}
/**
* Determines the mapping of data types to their Views handlers.
*
* @return array
* An associative array with data types as the keys and Views field data
* definitions as the values. In addition to all normally defined data types,
* keys can also be "options" for any field with an options list, "entity" for
* general entity-typed fields or "entity:ENTITY_TYPE" (with "ENTITY_TYPE"
* being the machine name of an entity type) for entities of that type.
*
* @see xntt_views_views_handler_mapping_alter()
*/
function _xntt_views_views_handler_mapping() {
$mapping = &drupal_static(__FUNCTION__);
if ($mapping === NULL) {
$mapping = [
'boolean' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_boolean',
],
'sort' => [
'id' => 'xntt_views',
],
],
'date' => [
'argument' => [
'id' => 'xntt_views_date',
],
'filter' => [
'id' => 'xntt_views_date',
],
'sort' => [
'id' => 'xntt_views',
],
],
'decimal' => [
'argument' => [
'id' => 'xntt_views',
'filter' => 'floatval',
],
'filter' => [
'id' => 'xntt_views_numeric',
],
'sort' => [
'id' => 'xntt_views',
],
],
'integer' => [
'argument' => [
'id' => 'xntt_views',
'filter' => 'intval',
],
'filter' => [
'id' => 'xntt_views_numeric',
],
'sort' => [
'id' => 'xntt_views',
],
],
'string' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_string',
],
'sort' => [
'id' => 'xntt_views',
],
],
'text' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_text',
],
'sort' => [
'id' => 'xntt_views',
],
],
'language' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_language',
'allow empty' => FALSE,
],
'sort' => [
'id' => 'xntt_views',
],
],
'options' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_options',
],
'sort' => [
'id' => 'xntt_views',
],
],
'entity:taxonomy_term' => [
'argument' => [
'id' => 'xntt_views_term',
],
'filter' => [
'id' => 'xntt_views_term',
],
'sort' => [
'id' => 'xntt_views',
],
],
'entity:user' => [
'argument' => [
'id' => 'xntt_views',
'filter' => 'intval',
],
'filter' => [
'id' => 'xntt_views_user',
],
'sort' => [
'id' => 'xntt_views',
],
],
'bundle_of' => [
'argument' => [
'id' => 'xntt_views',
],
'filter' => [
'id' => 'xntt_views_options',
],
'sort' => [
'id' => 'xntt_views',
],
],
];
}
return $mapping;
}
/**
* Creates a Views table definition for an entity type.
*
* @param string $entity_type_id
* The ID of the entity type.
* @param array $data
* The existing Views data definitions, passed by reference.
*
* @return array
* A Views table definition for the given entity type. Or an empty array if
* the entity type could not be found.
*/
function _xntt_views_views_entity_type_table($entity_type_id, array &$data) {
$entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
if (!$entity_type || !$entity_type->entityClassImplements(FieldableEntityInterface::class)) {
return [];
}
$table = [];
return $table;
}
/**
* Makes necessary field-specific adjustments to Views handler definitions.
*
* @param string $type
* The type of field, as defined in _xntt_views_views_handler_mapping().
* @param \Drupal\Core\Field\FieldInterface $field
* The field whose handler definitions are being created.
* @param array $definitions
* The handler definitions for the field, as a reference.
* @param string|null $sub_type
* (optional) If applicable, the field's sub-type.
*/
function _xntt_views_views_handler_adjustments($type, FieldInterface $field, array &$definitions, ?string $sub_type = NULL) {
// By default, all fields can be empty (or at least have to be treated that
// way).
if (!isset($definitions['filter']['allow empty'])) {
$definitions['filter']['allow empty'] = TRUE;
}
// For taxonomy term references, set the referenced vocabulary.
$data_definition = $field->getDataDefinition();
if ($type == 'entity:taxonomy_term') {
if (isset($data_definition->getSettings()['handler_settings']['target_bundles'])) {
$target_bundles = $data_definition->getSettings()['handler_settings']['target_bundles'];
if (count($target_bundles) == 1) {
$definitions['filter']['vocabulary'] = reset($target_bundles);
}
}
}
elseif ($type == 'options') {
if ($data_definition instanceof FieldItemDataDefinition) {
// If this is a normal Field API field, dynamically retrieve the options
// list at query time.
$field_definition = $data_definition->getFieldDefinition();
$bundle = $field_definition->getTargetBundle();
$field_name = $field_definition->getName();
$entity_type = $field_definition->getTargetEntityTypeId();
$definitions['filter']['options callback'] = '_xntt_views_views_get_allowed_values';
$definitions['filter']['options arguments'] = [$entity_type, $bundle, $field_name];
}
else {
// Otherwise, include the options list verbatim in the Views data, unless
// it's too big (or doesn't look valid).
$options = $data_definition->getSetting('allowed_values');
if (is_array($options) && count($options) <= 50) {
// Since the Views InOperator filter plugin doesn't allow just including
// the options in the definition, we use this workaround.
$definitions['filter']['options callback'] = 'array_filter';
$definitions['filter']['options arguments'] = [$options];
}
}
}
elseif ($type === 'bundle_of' && $sub_type) {
$definitions['filter']['options callback'] = '_xntt_views_views_get_bundle_names';
$definitions['filter']['options arguments'] = [$sub_type];
}
}
