Chris@18: installEntitySchema('user'); Chris@18: $this->installEntitySchema('node'); Chris@18: $this->installSchema('node', ['node_access']); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Unit testing the views data structure. Chris@18: * Chris@18: * We check data structure for both node and node revision tables. Chris@18: */ Chris@18: public function testViewsData() { Chris@18: $field_storage_string = FieldStorageConfig::create([ Chris@18: 'field_name' => 'field_string', Chris@18: 'entity_type' => 'node', Chris@18: 'type' => 'string', Chris@18: ]); Chris@18: $field_storage_string->save(); Chris@18: Chris@18: $field_storage_string_long = FieldStorageConfig::create([ Chris@18: 'field_name' => 'field_string_long', Chris@18: 'entity_type' => 'node', Chris@18: 'type' => 'string_long', Chris@18: ]); Chris@18: $field_storage_string_long->save(); Chris@18: Chris@18: NodeType::create(['type' => 'page'])->save(); Chris@18: NodeType::create(['type' => 'article'])->save(); Chris@18: Chris@18: // Attach the field to nodes. Chris@18: FieldConfig::create([ Chris@18: 'field_name' => 'field_string', Chris@18: 'entity_type' => 'node', Chris@18: 'bundle' => 'page', Chris@18: 'label' => 'GiraffeA" label', Chris@18: ])->save(); Chris@18: Chris@18: // Attach the string_long field to the page node type. Chris@18: FieldConfig::create([ Chris@18: 'field_name' => 'field_string_long', Chris@18: 'entity_type' => 'node', Chris@18: 'bundle' => 'page', Chris@18: 'label' => 'string_long label', Chris@18: ])->save(); Chris@18: Chris@18: // Attach the same field to a different bundle with a different label. Chris@18: FieldConfig::create([ Chris@18: 'field_name' => 'field_string', Chris@18: 'entity_type' => 'node', Chris@18: 'bundle' => 'article', Chris@18: 'label' => 'GiraffeB" label', Chris@18: ])->save(); Chris@18: Chris@18: // Now create some example nodes/users for the view result. Chris@18: for ($i = 0; $i < 5; $i++) { Chris@18: $edit = [ Chris@18: 'field_string' => [(['value' => $this->randomMachineName()])], Chris@18: ]; Chris@18: $nodes[] = Node::create(['type' => 'page'] + $edit); Chris@18: } Chris@18: Chris@18: /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ Chris@18: $table_mapping = $this->container->get('entity_type.manager') Chris@18: ->getStorage('node') Chris@18: ->getTableMapping(); Chris@18: Chris@18: $current_table = $table_mapping->getDedicatedDataTableName($field_storage_string); Chris@18: $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_string); Chris@18: $data = $this->getViewsData(); Chris@18: Chris@18: $this->assertArrayHasKey($current_table, $data); Chris@18: $this->assertArrayHasKey($revision_table, $data); Chris@18: Chris@18: // The node field should join against node_field_data. Chris@18: $this->assertArrayHasKey('node_field_data', $data[$current_table]['table']['join']); Chris@18: $this->assertArrayHasKey('node_field_revision', $data[$revision_table]['table']['join']); Chris@18: Chris@18: $expected_join = [ Chris@18: 'table' => $current_table, Chris@18: 'left_field' => 'nid', Chris@18: 'field' => 'entity_id', Chris@18: 'extra' => [ Chris@18: ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], Chris@18: ['left_field' => 'langcode', 'field' => 'langcode'], Chris@18: ], Chris@18: ]; Chris@18: $this->assertSame($expected_join, $data[$current_table]['table']['join']['node_field_data']); Chris@18: $expected_join = [ Chris@18: 'table' => $revision_table, Chris@18: 'left_field' => 'vid', Chris@18: 'field' => 'revision_id', Chris@18: 'extra' => [ Chris@18: ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE], Chris@18: ['left_field' => 'langcode', 'field' => 'langcode'], Chris@18: ], Chris@18: ]; Chris@18: $this->assertSame($expected_join, $data[$revision_table]['table']['join']['node_field_revision']); Chris@18: Chris@18: // Test click sortable for string field. Chris@18: $this->assertTrue($data[$current_table][$field_storage_string->getName()]['field']['click sortable']); Chris@18: // Click sort should only be on the primary field. Chris@18: $this->assertTrue(empty($data[$revision_table][$field_storage_string->getName()]['field']['click sortable'])); Chris@18: // Test click sortable for long text field. Chris@18: $data_long = $this->getViewsData('field_string_long'); Chris@18: $current_table_long = $table_mapping->getDedicatedDataTableName($field_storage_string_long); Chris@18: $this->assertTrue($data_long[$current_table_long][$field_storage_string_long->getName()]['field']['click sortable']); Chris@18: Chris@18: $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName()]['help']); Chris@18: $this->assertEquals('Appears in: page, article. Also known as: Content: GiraffeB" label', $data[$current_table][$field_storage_string->getName()]['help']); Chris@18: Chris@18: $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName() . '_value']['help']); Chris@18: $this->assertEquals('Appears in: page, article. Also known as: Content: GiraffeA" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['help']); Chris@18: Chris@18: // Since each label is only used once, views_entity_field_label() will Chris@18: // return a label using alphabetical sorting. Chris@18: $this->assertEquals('GiraffeA" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['title']); Chris@18: Chris@18: // Attach the same field to a different bundle with a different label. Chris@18: NodeType::create(['type' => 'news'])->save(); Chris@18: FieldConfig::create([ Chris@18: 'field_name' => $field_storage_string->getName(), Chris@18: 'entity_type' => 'node', Chris@18: 'bundle' => 'news', Chris@18: 'label' => 'GiraffeB" label', Chris@18: ])->save(); Chris@18: $this->container->get('views.views_data')->clear(); Chris@18: $data = $this->getViewsData(); Chris@18: Chris@18: // Now the 'GiraffeB" label' is used twice and therefore will be Chris@18: // selected by views_entity_field_label(). Chris@18: $this->assertEquals('GiraffeB" label (field_string)', $data[$current_table][$field_storage_string->getName() . '_value']['title']); Chris@18: $this->assertInstanceOf(MarkupInterface::class, $data[$current_table][$field_storage_string->getName()]['help']); Chris@18: $this->assertEquals('Appears in: page, article, news. Also known as: Content: GiraffeA" label', $data[$current_table][$field_storage_string->getName()]['help']); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Gets the views data for the field created in setUp(). Chris@18: * Chris@18: * @param string $field_storage_key Chris@18: * (optional) The optional field name. Chris@18: * Chris@18: * @return array Chris@18: * Views data. Chris@18: */ Chris@18: protected function getViewsData($field_storage_key = 'field_string') { Chris@18: $views_data = $this->container->get('views.views_data'); Chris@18: $data = []; Chris@18: Chris@18: // Check the table and the joins of the first field. Attached to node only. Chris@18: /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ Chris@18: $table_mapping = $this->container->get('entity_type.manager')->getStorage('node')->getTableMapping(); Chris@18: $field_storage = FieldStorageConfig::loadByName('node', $field_storage_key); Chris@18: $current_table = $table_mapping->getDedicatedDataTableName($field_storage); Chris@18: $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage); Chris@18: $data[$current_table] = $views_data->get($current_table); Chris@18: $data[$revision_table] = $views_data->get($revision_table); Chris@18: return $data; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Tests filtering entries with different translatability. Chris@18: */ Chris@18: public function testEntityFieldFilter() { Chris@18: NodeType::create(['type' => 'bundle1'])->save(); Chris@18: NodeType::create(['type' => 'bundle2'])->save(); Chris@18: Chris@18: // Create some example content. Chris@18: ConfigurableLanguage::create(['id' => 'es'])->save(); Chris@18: ConfigurableLanguage::create(['id' => 'fr'])->save(); Chris@18: Chris@18: ContentLanguageSettings::loadByEntityTypeBundle('node', 'bundle1') Chris@18: ->setDefaultLangcode('es') Chris@18: ->setLanguageAlterable(TRUE) Chris@18: ->save(); Chris@18: ContentLanguageSettings::loadByEntityTypeBundle('node', 'bundle2') Chris@18: ->setDefaultLangcode('es') Chris@18: ->setLanguageAlterable(TRUE) Chris@18: ->save(); Chris@18: Chris@18: $field_translation_map = [ Chris@18: 1 => ['bundle1' => TRUE, 'bundle2' => TRUE], Chris@18: 2 => ['bundle1' => FALSE, 'bundle2' => FALSE], Chris@18: 3 => ['bundle1' => TRUE, 'bundle2' => FALSE], Chris@18: ]; Chris@18: Chris@18: for ($i = 1; $i < 4; $i++) { Chris@18: $field_name = "field_name_$i"; Chris@18: FieldStorageConfig::create([ Chris@18: 'field_name' => $field_name, Chris@18: 'entity_type' => 'node', Chris@18: 'type' => 'string', Chris@18: ])->save(); Chris@18: Chris@18: foreach (['bundle1', 'bundle2'] as $bundle) { Chris@18: FieldConfig::create([ Chris@18: 'field_name' => $field_name, Chris@18: 'entity_type' => 'node', Chris@18: 'bundle' => $bundle, Chris@18: 'translatable' => $field_translation_map[$i][$bundle], Chris@18: ])->save(); Chris@18: } Chris@18: } Chris@18: Chris@18: $node1 = Node::create([ Chris@18: 'title' => 'Test title bundle1', Chris@18: 'type' => 'bundle1', Chris@18: 'langcode' => 'es', Chris@18: 'field_name_1' => 'field name 1: es', Chris@18: 'field_name_2' => 'field name 2: es', Chris@18: 'field_name_3' => 'field name 3: es', Chris@18: ]); Chris@18: $node1->save(); Chris@18: /** @var \Drupal\node\NodeInterface $translation */ Chris@18: $node1->addTranslation('fr', [ Chris@18: 'title' => $node1->title->value, Chris@18: 'field_name_1' => 'field name 1: fr', Chris@18: 'field_name_3' => 'field name 3: fr', Chris@18: ])->save(); Chris@18: Chris@18: $node2 = Node::create([ Chris@18: 'title' => 'Test title bundle2', Chris@18: 'type' => 'bundle2', Chris@18: 'langcode' => 'es', Chris@18: 'field_name_1' => 'field name 1: es', Chris@18: 'field_name_2' => 'field name 2: es', Chris@18: 'field_name_3' => 'field name 3: es', Chris@18: ]); Chris@18: $node2->save(); Chris@18: Chris@18: $node2->addTranslation('fr', [ Chris@18: 'title' => $node2->title->value, Chris@18: 'field_name_1' => 'field name 1: fr', Chris@18: ])->save(); Chris@18: Chris@18: $map = [ Chris@18: 'nid' => 'nid', Chris@18: 'langcode' => 'langcode', Chris@18: ]; Chris@18: Chris@18: $view = Views::getView('test_field_config_translation_filter'); Chris@18: Chris@18: // Filter by 'field name 1: es'. Chris@18: $view->setDisplay('embed_1'); Chris@18: $this->executeView($view); Chris@18: $expected = [ Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: ]; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: Chris@18: // Filter by 'field name 1: fr'. Chris@18: $view->setDisplay('embed_2'); Chris@18: $this->executeView($view); Chris@18: $expected = [ Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: ]; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: Chris@18: // Filter by 'field name 2: es'. Chris@18: $view->setDisplay('embed_3'); Chris@18: $this->executeView($view); Chris@18: $expected = [ Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: ]; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: Chris@18: // Filter by 'field name 2: fr', which doesn't exist. Chris@18: $view->setDisplay('embed_4'); Chris@18: $this->executeView($view); Chris@18: $expected = []; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: Chris@18: // Filter by 'field name 3: es'. Chris@18: $view->setDisplay('embed_5'); Chris@18: $this->executeView($view); Chris@18: $expected = [ Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'es', Chris@18: ], Chris@18: // Why is this one returned? Chris@18: [ Chris@18: 'nid' => $node2->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: ]; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: Chris@18: // Filter by 'field name 3: fr'. Chris@18: $view->setDisplay('embed_6'); Chris@18: $this->executeView($view); Chris@18: $expected = [ Chris@18: [ Chris@18: 'nid' => $node1->id(), Chris@18: 'langcode' => 'fr', Chris@18: ], Chris@18: ]; Chris@18: Chris@18: $this->assertIdenticalResultset($view, $expected, $map); Chris@18: $view->destroy(); Chris@18: } Chris@18: Chris@18: }