/** * Fetches a handler from the data cache. * * @param array $item * An associative array representing the handler to be retrieved: * - table: The name of the table containing the handler. * - field: The name of the field the handler represents. * @param string|null $override * (optional) Override the actual handler object with this plugin ID. Used for * aggregation when the handler is redirected to the aggregation handler. * * @return \Drupal\views\Plugin\views\HandlerBase * An instance of a handler object. May be a broken handler instance. */ public function getHandler($item, $override = NULL) { $table = $item['table']; $field = $item['field']; // Get the plugin manager for this type. $data = $this->viewsData->get($table); if (isset($data[$field][$this->handlerType])) { $definition = $data[$field][$this->handlerType]; foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table') as $key) { if (!isset($definition[$key])) { // First check the field level. if (!empty($data[$field][$key])) { $definition[$key] = $data[$field][$key]; } elseif (!empty($data['table'][$key])) { $definition[$key] = $data['table'][$key]; } } } // @todo This is crazy. Find a way to remove the override functionality. $plugin_id = $override ?: $definition['id']; // Try to use the overridden handler. try { return $this->createInstance($plugin_id, $definition); } catch (PluginException $e) { // If that fails, use the original handler. try { return $this->createInstance($definition['id'], $definition); } catch (PluginException $e) { // Deliberately empty, this case is handled generically below. } } } // Finally, use the 'broken' handler. return $this->createInstance('broken', array('original_configuration' => $item)); }
/** * Fetches a handler from the data cache. * * @param array $item * An associative array representing the handler to be retrieved: * - table: The name of the table containing the handler. * - field: The name of the field the handler represents. * @param string|null $override * (optional) Override the actual handler object with this plugin ID. Used for * aggregation when the handler is redirected to the aggregation handler. * * @return \Drupal\views\Plugin\views\ViewsHandlerInterface * An instance of a handler object. May be a broken handler instance. */ public function getHandler($item, $override = NULL) { $table = $item['table']; $field = $item['field']; // Get the plugin manager for this type. $data = $this->viewsData->get($table); if (isset($data[$field][$this->handlerType])) { $definition = $data[$field][$this->handlerType]; foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table', 'entity type', 'entity field') as $key) { if (!isset($definition[$key])) { // First check the field level. if (!empty($data[$field][$key])) { $definition[$key] = $data[$field][$key]; } // Then if that doesn't work, check the table level. elseif (!empty($data['table'][$key])) { $definition_key = $key === 'entity type' ? 'entity_type' : $key; $definition[$definition_key] = $data['table'][$key]; } } } // @todo This is crazy. Find a way to remove the override functionality. $plugin_id = $override ?: $definition['id']; // Try to use the overridden handler. $handler = $this->createInstance($plugin_id, $definition); if ($override && method_exists($handler, 'broken') && $handler->broken()) { $handler = $this->createInstance($definition['id'], $definition); } return $handler; } // Finally, use the 'broken' handler. return $this->createInstance('broken', array('original_configuration' => $item)); }
/** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { // Just add support for entity types which have a views integration. if (($base_table = $entity_type->getBaseTable()) && $this->viewsData->get($base_table) && $this->entityManager->hasHandler($entity_type_id, 'view_builder')) { $this->derivatives[$entity_type_id] = array('id' => 'entity:' . $entity_type_id, 'provider' => 'views', 'title' => $entity_type->getLabel(), 'help' => t('Display the @label', array('@label' => $entity_type->getLabel())), 'base' => array($entity_type->getDataTable() ?: $entity_type->getBaseTable()), 'entity_type' => $entity_type_id, 'display_types' => array('normal'), 'class' => $base_plugin_definition['class']); } } return $this->derivatives; }
/** * Set all derivatives for an entity type. * * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type * @param array $base_plugin_definition */ protected function setConfigurableFieldsDerivatives(EntityTypeInterface $entity_type, array $base_plugin_definition) { /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage */ $field_storages = $this->field_manager->getFieldStorageDefinitions($entity_type->id()); foreach ($field_storages as $field_id => $field_storage) { if ($field_storage->getType() == 'datetime') { $entity_type_id = $entity_type->id(); // Find better way to get table name. $field_table = $entity_type_id . '__' . $field_id; $field_table_data = $this->viewsData->get($field_table); if (isset($field_table_data[$field_id])) { $derivative = []; $field_info = $field_table_data[$field_id]; $derivative['field_id'] = $field_id; $join_tables = array_keys($field_table_data['table']['join']); // @todo Will there ever be more than 1 tables here? $join_table = array_pop($join_tables); $join_table_data = $this->viewsData->get($join_table); $derivative = ['replacements' => ['field_id' => $field_id, 'entity_type' => $entity_type_id, 'entity_label' => $entity_type->getLabel(), 'field_label' => $field_info['title'], 'base_table' => $join_table, 'field_table' => $field_table, 'default_field_id' => $this->getTableDefaultField($join_table_data, $entity_type_id), 'base_field' => $this->getTableBaseField($join_table_data)], 'view_template_id' => 'calendar_config_field']; $this->setDerivative($derivative, $base_plugin_definition); //$this->setDerivative($field_info, $entity_type, $field_table_data, $base_plugin_definition); } } } }
/** * Returns the form to edit a view. * * @param \Drupal\views_ui\ViewUI $view * The view to be edited. * @param string|null $display_id * (optional) The display ID being edited. Defaults to NULL, which will load * the first available display. * * @return array * An array containing the Views edit and preview forms. */ public function edit(ViewUI $view, $display_id = NULL) { $name = $view->label(); $data = $this->viewsData->get($view->get('base_table')); if (isset($data['table']['base']['title'])) { $name .= ' (' . $data['table']['base']['title'] . ')'; } $build['#title'] = $name; $build['edit'] = $this->entityFormBuilder()->getForm($view, 'edit', array('display_id' => $display_id)); $build['preview'] = $this->entityFormBuilder()->getForm($view, 'preview', array('display_id' => $display_id)); return $build; }
/** * Tests getHandler() without an override. */ public function testGetHandlerNoOverride() { $this->setupMockedFactory(); $item = []; $item['table'] = 'test_table'; $item['field'] = 'test_field'; $views_data = []; $views_data['test_field']['test']['id'] = 'test_id'; $this->viewsData->expects($this->once())->method('get')->with('test_table')->willReturn($views_data); $plugin = $this->getMock('Drupal\\views\\Plugin\\views\\ViewsHandlerInterface'); $this->factory->expects($this->once())->method('createInstance')->with('test_id')->willReturn($plugin); $result = $this->handlerManager->getHandler($item); $this->assertSame($plugin, $result); }
/** * Tests that getting all data has same results as getting data with NULL * logic. * * @covers ::getAll */ public function testGetAllEqualsToGetNull() { $expected_views_data = $this->viewsDataWithProvider(); $this->setupMockedModuleHandler(); // Setup a warm cache backend for a single table. $this->cacheBackend->expects($this->once())->method('get')->with("views_data:en"); $this->cacheBackend->expects($this->once())->method('set')->with('views_data:en', $expected_views_data); // Initialize the views data cache and repeat with no specified table. This // should only load the cache entry for all tables. for ($i = 0; $i < 5; $i++) { $this->assertSame($expected_views_data, $this->viewsData->getAll()); $this->assertSame($expected_views_data, $this->viewsData->get()); } }
/** * Tests the cache calls for multiple tables without warm caches. * * @covers ::get */ public function testCacheCallsWithoutWarmCacheAndGetMultipleTables() { $expected_views_data = $this->viewsDataWithProvider(); $table_name = 'views_test_data'; $table_name_2 = 'views_test_data_2'; // Setup a warm cache backend for all table data, but not single tables. $this->cacheBackend->expects($this->at(0))->method('get')->with("views_data:{$table_name}:en")->will($this->returnValue(FALSE)); $this->cacheBackend->expects($this->at(1))->method('get')->with('views_data:en')->will($this->returnValue((object) array('data' => $expected_views_data))); $this->cacheBackend->expects($this->at(2))->method('set')->with("views_data:{$table_name}:en", $expected_views_data[$table_name]); $this->cacheBackend->expects($this->at(3))->method('get')->with("views_data:{$table_name_2}:en")->will($this->returnValue(FALSE)); $this->cacheBackend->expects($this->at(4))->method('set')->with("views_data:{$table_name_2}:en", $expected_views_data[$table_name_2]); $this->assertSame($expected_views_data[$table_name], $this->viewsData->get($table_name)); $this->assertSame($expected_views_data[$table_name_2], $this->viewsData->get($table_name_2)); // Should only be invoked the first time. $this->assertSame($expected_views_data[$table_name], $this->viewsData->get($table_name)); $this->assertSame($expected_views_data[$table_name_2], $this->viewsData->get($table_name_2)); }
/** * @covers ::addHandler */ public function testAddHandlerWithEntityField() { /** @var \Drupal\views\ViewExecutable|\PHPUnit_Framework_MockObject_MockObject $view */ /** @var \Drupal\views\Plugin\views\display\DisplayPluginBase|\PHPUnit_Framework_MockObject_MockObject $display */ list($view, $display) = $this->setupBaseViewAndDisplay(); $views_data = []; $views_data['table']['entity type'] = 'test_entity_type'; $views_data['test_field'] = ['entity field' => 'test_field', 'field' => ['id' => 'standard'], 'filter' => ['id' => 'standard'], 'argument' => ['id' => 'standard'], 'sort' => ['id' => 'standard']]; $this->viewsData->expects($this->atLeastOnce())->method('get')->with('test_entity')->willReturn($views_data); foreach (['field', 'filter', 'argument', 'sort'] as $handler_type) { $display->expects($this->atLeastOnce())->method('setOption')->with($this->callback(function ($argument) { return $argument; }), ['test_field' => ['id' => 'test_field', 'table' => 'test_entity', 'field' => 'test_field', 'entity_type' => 'test_entity_type', 'entity_field' => 'test_field', 'plugin_id' => 'standard']]); } foreach (['field', 'filter', 'argument', 'sort'] as $handler_type) { $view->addHandler('default', $handler_type, 'test_entity', 'test_field'); } }
/** * Adds an instance of a handler to the view. * * Items may be fields, filters, sort criteria, or arguments. * * @param string $display_id * The machine name of the display. * @param string $type * The type of handler being added. * @param string $table * The name of the table this handler is from. * @param string $field * The name of the field this handler is from. * @param array $options * (optional) Extra options for this instance. Defaults to an empty array. * @param string $id * (optional) A unique ID for this handler instance. Defaults to NULL, in * which case one will be generated. * * @return string * The unique ID for this handler instance. */ public function addHandler($display_id, $type, $table, $field, $options = array(), $id = NULL) { $types = $this::getHandlerTypes(); $this->setDisplay($display_id); $data = $this->viewsData->get($table); $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']); if (empty($id)) { $id = $this->generateHandlerId($field, $fields); } // If the desired type is not found, use the original value directly. $handler_type = !empty($types[$type]['type']) ? $types[$type]['type'] : $type; $fields[$id] = array('id' => $id, 'table' => $table, 'field' => $field) + $options; if (isset($data['table']['entity type'])) { $fields[$id]['entity_type'] = $data['table']['entity type']; } if (isset($data[$field]['entity field'])) { $fields[$id]['entity_field'] = $data[$field]['entity field']; } // Load the plugin ID if available. if (isset($data[$field][$handler_type]['id'])) { $fields[$id]['plugin_id'] = $data[$field][$handler_type]['id']; } $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields); return $id; }
/** * Fetches a list of all fields available for a given base type. * * @param (array|string) $base * A list or a single base_table, for example node. * @param string $type * The handler type, for example field or filter. * @param bool $grouping * Should the result grouping by its 'group' label. * @param string $sub_type * An optional sub type. E.g. Allows making an area plugin available for * header only, instead of header, footer, and empty regions. * * @return array * A keyed array of in the form of 'base_table' => 'Description'. */ public function fetchFields($base, $type, $grouping = FALSE, $sub_type = NULL) { if (!$this->fields) { $data = $this->data->get(); // This constructs this ginormous multi dimensional array to // collect the important data about fields. In the end, // the structure looks a bit like this (using nid as an example) // $strings['nid']['filter']['title'] = 'string'. // // This is constructed this way because the above referenced strings // can appear in different places in the actual data structure so that // the data doesn't have to be repeated a lot. This essentially lets // each field have a cheap kind of inheritance. foreach ($data as $table => $table_data) { $bases = array(); $strings = array(); $skip_bases = array(); foreach ($table_data as $field => $info) { // Collect table data from this table if ($field == 'table') { // calculate what tables this table can join to. if (!empty($info['join'])) { $bases = array_keys($info['join']); } // And it obviously joins to itself. $bases[] = $table; continue; } foreach (array('field', 'sort', 'filter', 'argument', 'relationship', 'area') as $key) { if (!empty($info[$key])) { if ($grouping && !empty($info[$key]['no group by'])) { continue; } if ($sub_type && isset($info[$key]['sub_type']) && !in_array($sub_type, (array) $info[$key]['sub_type'])) { continue; } if (!empty($info[$key]['skip base'])) { foreach ((array) $info[$key]['skip base'] as $base_name) { $skip_bases[$field][$key][$base_name] = TRUE; } } elseif (!empty($info['skip base'])) { foreach ((array) $info['skip base'] as $base_name) { $skip_bases[$field][$key][$base_name] = TRUE; } } foreach (array('title', 'group', 'help', 'base', 'aliases') as $string) { // First, try the lowest possible level if (!empty($info[$key][$string])) { $strings[$field][$key][$string] = $info[$key][$string]; } elseif (!empty($info[$string])) { $strings[$field][$key][$string] = $info[$string]; } elseif (!empty($table_data['table'][$string])) { $strings[$field][$key][$string] = $table_data['table'][$string]; } elseif ($string == 'help') { $strings[$field][$key][$string] = ''; } else { if ($string != 'base') { $strings[$field][$key][$string] = SafeMarkup::format("Error: missing @component", array('@component' => $string)); } } } } } } foreach ($bases as $base_name) { foreach ($strings as $field => $field_strings) { foreach ($field_strings as $type_name => $type_strings) { if (empty($skip_bases[$field][$type_name][$base_name])) { $this->fields[$base_name][$type_name]["{$table}.{$field}"] = $type_strings; } } } } } } // If we have an array of base tables available, go through them // all and add them together. Duplicate keys will be lost and that's // Just Fine. if (is_array($base)) { $strings = array(); foreach ($base as $base_table) { if (isset($this->fields[$base_table][$type])) { $strings += $this->fields[$base_table][$type]; } } uasort($strings, array('self', 'fetchedFieldSort')); return $strings; } if (isset($this->fields[$base][$type])) { uasort($this->fields[$base][$type], array($this, 'fetchedFieldSort')); return $this->fields[$base][$type]; } return array(); }