Commit b20755f4 authored by catch's avatar catch

Issue #2142549 by amateescu, yched: Support multi-column field types in base tables.

parent a1b0d248
......@@ -281,11 +281,21 @@ protected function mapFromStorageRecords(array $records, $load_revision = FALSE)
$entities = array();
foreach ($records as $id => $record) {
$entities[$id] = array();
// Skip the item delta and item value levels (if possible) but let the
// field assign the value as suiting. This avoids unnecessary array
// hierarchies and saves memory here.
foreach ($record as $name => $value) {
// Skip the item delta and item value levels but let the field assign
// the value as suiting. This avoids unnecessary array hierarchies and
// saves memory here.
$entities[$id][$name][Language::LANGCODE_DEFAULT] = $value;
// Handle columns named [field_name]__[column_name] (e.g for field types
// that store several properties).
if ($field_name = strstr($name, '__', TRUE)) {
$property_name = substr($name, strpos($name, '__') + 2);
$entities[$id][$field_name][Language::LANGCODE_DEFAULT][$property_name] = $value;
}
else {
// Handle columns named directly after the field (e.g if the field
// type only stores one property).
$entities[$id][$name][Language::LANGCODE_DEFAULT] = $value;
}
}
// If we have no multilingual values we can instantiate entity objecs
// right now, otherwise we need to collect all the field values first.
......@@ -332,13 +342,13 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
}
$data = $query->execute();
$field_definition = \Drupal::entityManager()->getFieldDefinitions($this->entityType);
$field_definitions = \Drupal::entityManager()->getFieldDefinitions($this->entityType);
$translations = array();
if ($this->revisionDataTable) {
$data_fields = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
$data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityInfo['revision_data_table']), drupal_schema_fields_sql($this->entityInfo['base_table'])));
}
else {
$data_fields = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
$data_column_names = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
}
foreach ($data as $values) {
......@@ -349,9 +359,24 @@ protected function attachPropertyData(array &$entities, $revision_id = FALSE) {
$langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
$translations[$id][$langcode] = TRUE;
foreach ($field_definition as $name => $definition) {
if (isset($data_fields[$name])) {
$entities[$id][$name][$langcode] = $values[$name];
foreach (array_keys($field_definitions) as $field_name) {
// Handle columns named directly after the field.
if (isset($data_column_names[$field_name])) {
$entities[$id][$field_name][$langcode] = $values[$field_name];
}
else {
// @todo Change this logic to be based on a mapping of field
// definition properties (translatability, revisionability) in
// https://drupal.org/node/2144631.
foreach ($data_column_names as $data_column_name) {
// Handle columns named [field_name]__[column_name], for which we
// need to look through all column names from the table that start
// with the name of the field.
if (($data_field_name = strstr($data_column_name, '__', TRUE)) && $data_field_name === $field_name) {
$property_name = substr($data_column_name, strpos($data_column_name, '__') + 2);
$entities[$id][$field_name][$langcode][$property_name] = $values[$data_column_name];
}
}
}
}
}
......@@ -744,17 +769,41 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
*/
protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
$record = new \stdClass();
$values = array();
$definitions = $entity->getPropertyDefinitions();
$schema = drupal_get_schema($this->entityInfo[$table_key]);
$is_new = $entity->isNew();
$multi_column_fields = array();
foreach (drupal_schema_fields_sql($this->entityInfo[$table_key]) as $name) {
$info = $schema['fields'][$name];
$value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
// Check for fields which store data in multiple columns and process them
// separately.
if ($field = strstr($name, '__', TRUE)) {
$multi_column_fields[$field] = TRUE;
continue;
}
$values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
}
// Handle fields that store multiple properties and match each property name
// to its schema column name.
foreach (array_keys($multi_column_fields) as $field_name) {
$field_items = $entity->get($field_name);
$field_value = $field_items->getValue();
// @todo Reconsider the usage of getPropertyDefinitions() after
// https://drupal.org/node/2144327.
foreach (array_keys($field_items[0]->getPropertyDefinitions()) as $property_name) {
if (isset($schema['fields'][$field_name . '__' . $property_name])) {
$values[$field_name . '__' . $property_name] = isset($field_value[0][$property_name]) ? $field_value[0][$property_name] : NULL;
}
}
}
foreach ($values as $field_name => $value) {
// If we are creating a new entity, we must not populate the record with
// NULL values otherwise defaults would not be applied.
if (isset($value) || !$is_new) {
$record->$name = drupal_schema_get_field_value($info, $value);
$record->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value);
}
}
......
......@@ -22,7 +22,7 @@ class EntityReferenceItemTest extends FieldUnitTestBase {
*
* @var array
*/
public static $modules = array('entity_reference', 'taxonomy', 'options');
public static $modules = array('entity_reference', 'taxonomy', 'options', 'text', 'filter');
/**
* The taxonomy vocabulary to test with.
......
......@@ -387,7 +387,7 @@ function createForum($type, $parent = 0) {
);
// Verify forum.
$term = db_query("SELECT * FROM {taxonomy_term_data} t WHERE t.vid = :vid AND t.name = :name AND t.description = :desc", array(':vid' => \Drupal::config('forum.settings')->get('vocabulary'), ':name' => $name, ':desc' => $description))->fetchAssoc();
$term = db_query("SELECT * FROM {taxonomy_term_data} t WHERE t.vid = :vid AND t.name = :name AND t.description__value = :desc", array(':vid' => \Drupal::config('forum.settings')->get('vocabulary'), ':name' => $name, ':desc' => $description))->fetchAssoc();
$this->assertTrue(!empty($term), 'The ' . $type . ' exists in the database');
// Verify forum hierarchy.
......
......@@ -37,7 +37,7 @@ class TaxonomyTermReferenceRdfaTest extends FieldRdfaTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('taxonomy');
public static $modules = array('taxonomy', 'options', 'text', 'filter');
public static function getInfo() {
return array(
......
......@@ -224,14 +224,10 @@ public static function baseFieldDefinitions($entity_type) {
->setLabel(t('Name'))
->setDescription(t('The term name.'));
$fields['description'] = FieldDefinition::create('string')
$fields['description'] = FieldDefinition::create('text_long')
->setLabel(t('Description'))
->setDescription(t('A description of the term.'));
// @todo Combine with description.
$fields['format'] = FieldDefinition::create('string')
->setLabel(t('Description format'))
->setDescription(t('The filter format ID of the description.'));
->setDescription(t('A description of the term.'))
->setFieldSetting('text_processing', 1);
$fields['weight'] = FieldDefinition::create('integer')
->setLabel(t('Weight'))
......
......@@ -74,7 +74,7 @@ public function form(array $form, array &$form_state) {
'#type' => 'text_format',
'#title' => $this->t('Description'),
'#default_value' => $term->description->value,
'#format' => $term->format->value,
'#format' => $term->description->format,
'#weight' => 0,
);
$language_configuration = $this->moduleHandler->moduleExists('language') ? language_get_default_configuration('taxonomy_term', $vocabulary->id()) : FALSE;
......@@ -178,7 +178,7 @@ public function buildEntity(array $form, array &$form_state) {
// \Drupal\Core\Entity\Entity::save() method.
$description = $form_state['values']['description'];
$term->description->value = $description['value'];
$term->format->value = $description['format'];
$term->description->format = $description['format'];
// Assign parents with proper delta values starting from 0.
$term->parent = array_keys($form_state['values']['parent']);
......
......@@ -24,10 +24,12 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
foreach ($entities as $entity) {
// Add the description if enabled.
// @todo Remove this when base fields are able to use formatters.
// https://drupal.org/node/2144919
$display = $displays[$entity->bundle()];
if (!empty($entity->description->value) && $display->getComponent('description')) {
$entity->content['description'] = array(
'#markup' => check_markup($entity->description->value, $entity->format->value, '', TRUE),
'#markup' => $entity->description->processed,
'#prefix' => '<div class="taxonomy-term-description">',
'#suffix' => '</div>',
);
......
......@@ -23,7 +23,7 @@ class TaxonomyTermReferenceItemTest extends FieldUnitTestBase {
*
* @var array
*/
public static $modules = array('taxonomy', 'options');
public static $modules = array('taxonomy', 'options', 'text', 'filter');
public static function getInfo() {
return array(
......
......@@ -55,9 +55,11 @@ function createTerm($vocabulary) {
$format = array_pop($filter_formats);
$term = entity_create('taxonomy_term', array(
'name' => $this->randomName(),
'description' => $this->randomName(),
// Use the first available text format.
'format' => $format->format,
'description' => array(
'value' => $this->randomName(),
// Use the first available text format.
'format' => $format->format,
),
'vid' => $vocabulary->id(),
'langcode' => Language::LANGCODE_NOT_SPECIFIED,
));
......
......@@ -346,11 +346,16 @@ function testTermInterface() {
// Did this page request display a 'term-listing-heading'?
$this->assertPattern('|class="taxonomy-term-description"|', 'Term page displayed the term description element.');
// Check that it does NOT show a description when description is blank.
$term->description = '';
$term->description->value = NULL;
$term->save();
$this->drupalGet('taxonomy/term/' . $term->id());
$this->assertNoPattern('|class="taxonomy-term-description"|', 'Term page did not display the term description when description was blank.');
// Check that the description value is processed.
$term->description->value = $value = $this->randomName();
$term->save();
$this->assertEqual($term->description->processed, "<p>$value</p>\n");
// Check that the term feed page is working.
$this->drupalGet('taxonomy/term/' . $term->id() . '/feed');
......
......@@ -88,7 +88,7 @@ function testTaxonomyTokenReplacement() {
$tests = array();
$tests['[term:tid]'] = $term1->id();
$tests['[term:name]'] = check_plain($term1->name->value);
$tests['[term:description]'] = check_markup($term1->description->value, $term1->format->value);
$tests['[term:description]'] = $term1->description->processed;
$tests['[term:url]'] = url('taxonomy/term/' . $term1->id(), array('absolute' => TRUE));
$tests['[term:node-count]'] = 0;
$tests['[term:parent:name]'] = '[term:parent:name]';
......@@ -103,7 +103,7 @@ function testTaxonomyTokenReplacement() {
$tests = array();
$tests['[term:tid]'] = $term2->id();
$tests['[term:name]'] = check_plain($term2->name->value);
$tests['[term:description]'] = check_markup($term2->description->value, $term2->format->value);
$tests['[term:description]'] = $term2->description->processed;
$tests['[term:url]'] = url('taxonomy/term/' . $term2->id(), array('absolute' => TRUE));
$tests['[term:node-count]'] = 1;
$tests['[term:parent:name]'] = check_plain($term1->name->value);
......
......@@ -6,4 +6,5 @@ version: VERSION
core: 8.x
dependencies:
- options
- text
configure: taxonomy.vocabulary_list
......@@ -48,13 +48,13 @@ function taxonomy_schema() {
'default' => '',
'description' => 'The term name.',
),
'description' => array(
'description__value' => array(
'type' => 'text',
'not null' => FALSE,
'size' => 'big',
'description' => 'A description of the term.',
),
'format' => array(
'description__format' => array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
......@@ -428,3 +428,24 @@ function taxonomy_update_8009() {
}
}
}
/**
* Rename the 'description' and 'format' columns for taxonomy terms.
*/
function taxonomy_update_8010() {
$description = array(
'type' => 'text',
'not null' => FALSE,
'size' => 'big',
'description' => 'A description of the term.',
);
db_change_field('taxonomy_term_data', 'description', 'description__value', $description);
$format = array(
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'description' => 'The filter format ID of the description.',
);
db_change_field('taxonomy_term_data', 'format', 'description__format', $format);
}
......@@ -73,7 +73,7 @@ function taxonomy_term_feed(Term $term) {
$channel['title'] = \Drupal::config('system.site')->get('name') . ' - ' . $term->label();
// Only display the description if we have a single term, to avoid clutter and confusion.
// HTML will be removed from feed description.
$channel['description'] = check_markup($term->description->value, $term->format->value, '', TRUE);
$channel['description'] = $term->description->processed;
$nids = taxonomy_select_nodes($term->id(), FALSE, \Drupal::config('system.rss')->get('items.limit'));
return node_feed($nids, $channel);
......
......@@ -108,7 +108,7 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options =
break;
case 'description':
$replacements[$original] = $sanitize ? check_markup($term->description->value, $term->format->value, '', TRUE) : $term->description->value;
$replacements[$original] = $sanitize ? $term->description->processed : $term->description->value;
break;
case 'url':
......
......@@ -128,12 +128,12 @@ function taxonomy_views_data() {
);
// Term description
$data['taxonomy_term_data']['description'] = array(
$data['taxonomy_term_data']['description__value'] = array(
'title' => t('Term description'),
'help' => t('The description associated with a taxonomy term.'),
'field' => array(
'id' => 'markup',
'format' => array('field' => 'format'),
'format' => array('field' => 'description__format'),
'click sortable' => FALSE,
),
'filter' => array(
......
......@@ -21,7 +21,7 @@ class RowEntityTest extends ViewUnitTestBase {
*
* @var array
*/
public static $modules = array('taxonomy', 'field', 'entity', 'system');
public static $modules = array('taxonomy', 'text', 'filter', 'field', 'entity', 'system');
/**
* Views used by this test.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment