diff --git a/core/modules/views/lib/Drupal/views/EntityViewsController.php b/core/modules/views/lib/Drupal/views/EntityViewsController.php index 9fd9689..82e8424 100644 --- a/core/modules/views/lib/Drupal/views/EntityViewsController.php +++ b/core/modules/views/lib/Drupal/views/EntityViewsController.php @@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\Core\TypedData\TypedDataManager; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -50,13 +51,20 @@ class EntityViewsController implements EntityControllerInterface { protected $moduleHandler; /** - * The translation manager + * The translation manager. * * @var \Drupal\Core\StringTranslation\TranslationInterface */ protected $translationManager; /** + * The typed data manager. + * + * @var \Drupal\Core\TypedData\TypedDataManager + */ + protected $typedDataManager; + + /** * Constructs an EntityViewsController object. * * @param string $entity_type @@ -71,13 +79,14 @@ class EntityViewsController implements EntityControllerInterface { * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager * The translation manager. */ - function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage_controller, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, TranslationInterface $translation_manager) { + function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage_controller, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, TranslationInterface $translation_manager, TypedDataManager $typed_data_manager) { $this->entityType = $entity_type; $this->entityInfo = $entity_info; $this->entityManager = $entity_manager; $this->storageController = $storage_controller; $this->moduleHandler = $module_handler; $this->translationManager = $translation_manager; + $this->typedDataManager = $typed_data_manager; } /** @@ -90,7 +99,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ $container->get('entity.manager')->getStorageController($entity_type), $container->get('entity.manager'), $container->get('module_handler'), - $container->get('string_translation') + $container->get('string_translation'), + $container->get('typed_data') ); } @@ -155,25 +165,50 @@ public function viewsData() { // entity manager. // @todo We should better just rely on information coming from the entity // storage controller. + $schema = array(); foreach (array_keys($data) as $table) { - foreach ($this->drupalSchemaFieldsSql($table) as $field_name) { - if (isset($field_definitions[$field_name])) { - $views_field = &$data[$table][$field_name]; - // @todo Is translating the right way to handle label/description? - // This is/should be translated in the UI when it's displayed? - $views_field['title'] = $field_definitions[$field_name]['label']; - $views_field['help'] = $field_definitions[$field_name]['description']; - // @todo Find a proper find the mappings between typed data and views - // handlers. Maybe the data types should define it with fallback to - // standard or views should have the same naming scheme. - $views_field = $this->mapTypedDataToHandlerId($field_definitions[$field_name], $views_field); + if (!isset($schema[$table])) { + $schema[$table] = $this->drupalSchemaFieldsSql($table); + } + } + foreach ($field_definitions as $field_name => $definition) { + foreach ($schema as $table => $schema_fields) { + if (in_array($field_name, $schema_fields)) { + // @todo: may one field be in multiple tables, eg. revision + base? + $this->mapFieldDefinition($field_name, $field_definitions[$field_name], $data[$table]); } } } - return $data; } + protected function mapFieldDefinition($field_name, $field_definition, &$views_table_data) { + // Create a dummy instance to retrieve property definitions. + $field_item_list = $this->typedDataManager->createInstance($field_definition['type'], $field_definition); + $field_item = $field_item_list[0]; + $property_definitions = $field_item->getPropertyDefinitions(); + + // @todo: We should probably move this to subclasses. + switch ($field_definition['type']) { + case 'field_item:entity_reference': + $id_definition = array_slice($property_definitions, 0, 1); + $id_name = key($id_definition); + $reference_definition = array_slice($property_definitions, 1, 1); + $views_table_data[$field_name]['relationship'] = $this->generateRelationship($id_name, $id_definition, $reference_definition, $field_definition); + break; + default: + // Add all properties to views table data. + foreach ($property_definitions as $name => $definition) { + if (!isset($views_table_data[$field_name])) { + $views_table_data[$field_name] = $this->mapFieldPropertyDefinition($name, $definition, $field_definition); + } + // For now, we only support single property fields. + break; + } + } + // @todo Allow field types to customize this. + } + /** * Provides a mapping from typed data plugin types to view plugin types. * @@ -185,39 +220,63 @@ public function viewsData() { * @return array * The modified views data field definition. */ - protected function mapTypedDataToHandlerId($typed_data, $views_field) { - switch ($typed_data['type']) { - case 'integer_field': + protected function mapFieldPropertyDefinition($property_name, $property_definition, $field_definition) { + $views_field = array(); + + $views_field['title'] = $field_definition['label'] . ' (' . $property_definition['label'] . ')'; + if (!empty($field_definition['description'])) { + $views_field['help'] = $field_definition['description']; + } + + $definition = $this->typedDataManager->getDefinition($property_definition['type']); + + $data_type = $property_definition['type']; + + // Map data types to their primitives if possible, for example the email type + // will be mapped to its primitive type string. + // @todo: Typed data api should provide an api to retrieve primitive types. + if (is_subclass_of($definition['class'], 'Drupal\Core\TypedData\Type\FloatInterface')) { + $data_type = 'float'; + } + else if (is_subclass_of($definition['class'], 'Drupal\Core\TypedData\Type\IntegerInterface')) { + $data_type = 'integer'; + } + + switch ($data_type) { + case 'float': + case 'integer': $views_field['field']['id'] = 'numeric'; $views_field['argument']['id'] = 'numeric'; $views_field['filter']['id'] = 'numeric'; $views_field['sort']['id'] = 'standard'; break; - case 'string_field': + case 'string': $views_field['field']['id'] = 'standard'; $views_field['argument']['id'] = 'string'; $views_field['filter']['id'] = 'string'; $views_field['sort']['id'] = 'standard'; break; - case 'language_field': - $views_field['field']['id'] = 'language'; - $views_field['argument']['id'] = 'language'; - $views_field['filter']['id'] = 'language'; - $views_field['sort']['id'] = 'standard'; - break; - case 'boolean_field': + case 'boolean': $views_field['field']['id'] = 'boolean'; $views_field['argument']['id'] = 'numeric'; $views_field['filter']['id'] = 'boolean'; $views_field['sort']['id'] = 'standard'; break; - case 'uuid_field': + // @todo: Should be mapped. + case 'uuid': $views_field['field']['id'] = 'standard'; $views_field['argument']['id'] = 'string'; $views_field['filter']['id'] = 'string'; $views_field['sort']['id'] = 'standard'; break; - case 'entity_reference_field': + // @todo: Move this to language field? + case 'language': + $views_field['field']['id'] = 'language'; + $views_field['argument']['id'] = 'language'; + $views_field['filter']['id'] = 'language'; + $views_field['sort']['id'] = 'standard'; + break; + case 'entity_reference': // @todo No idea to determine how to find out whether the field is a number/string ID. // @todo Should the actual field handler respect that this is just renders a number // @todo Create an optional entity field handler, that can render the @@ -226,23 +285,33 @@ protected function mapTypedDataToHandlerId($typed_data, $views_field) { $views_field['argument']['id'] = 'numeric'; $views_field['filter']['id'] = 'numeric'; $views_field['sort']['id'] = 'standard'; - - $entity_type = $typed_data['settings']['target_type']; - $entity_info = $this->entityManager->getDefinition($entity_type); - $views_field['relationship'] = array( - 'base' => $entity_info['base_table'], - 'base field' => $entity_info['entity_keys']['id'], - 'label' => $typed_data['label'], - 'id' => 'standard', - ); - // @todo use the module handler to provide a way to hook in for other - // modules. Therefore the mapping has to be static defined. } return $views_field; } /** + * Generates a relationship for the given ID property. + * + * @param $property_name + * @param $property_definition + * @param $field_definition + */ + protected function generateRelationship($id_name, $id_definition, $reference_definition, $field_definition) { + // @todo: implement mapping. + return array(); + + $views_relationship = array( + 'base' => $entity_info['base_table'], + 'base field' => $entity_info['entity_keys']['id'], + 'label' => $typed_data['label'], + 'id' => 'standard', + ); + + return $views_relationship; + } + + /** * Translates a string to the current language or to a given language. * * See the t() documentation for details.