diff --git a/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php b/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php new file mode 100644 index 0000000..c7714c8 --- /dev/null +++ b/core/modules/views/src/Plugin/views/join/FieldOrLanguageJoin.php @@ -0,0 +1,152 @@ +configuration['table formula'])) { + $right_table = $this->table; + } + else { + $right_table = $this->configuration['table formula']; + } + + if ($this->leftTable) { + $left = $view_query->getTableInfo($this->leftTable); + $left_field = "$left[alias].$this->leftField"; + } + else { + // This can be used if left_field is a formula or something. It should be used only *very* rarely. + $left_field = $this->leftField; + } + + $condition = "$left_field = $table[alias].$this->field"; + $arguments = array(); + + // Tack on the extra. + if (isset($this->extra)) { + if (is_array($this->extra)) { + $extras = array(); + foreach ($this->extra as $info) { + // Do not require 'value' to be set; allow for field syntax instead. + $info += array( + 'value' => NULL, + ); + // Figure out the table name. Remember, only use aliases provided + // if at all possible. + $join_table = ''; + if (!array_key_exists('table', $info)) { + $join_table = $table['alias'] . '.'; + } + elseif (isset($info['table'])) { + // If we're aware of a table alias for this table, use the table + // alias instead of the table name. + if (isset($left) && $left['table'] == $info['table']) { + $join_table = $left['alias'] . '.'; + } + else { + $join_table = $info['table'] . '.'; + } + } + + // Convert a single-valued array of values to the single-value case, + // and transform from IN() notation to = notation + if (is_array($info['value']) && count($info['value']) == 1) { + if (empty($info['operator'])) { + $operator = '='; + } + else { + $operator = $info['operator'] == 'NOT IN' ? '!=' : '='; + } + $info['value'] = array_shift($info['value']); + } + + if (is_array($info['value'])) { + // With an array of values, we need multiple placeholders and the + // 'IN' operator is implicit. + $local_arguments = array(); + foreach ($info['value'] as $value) { + $placeholder_i = ':views_join_condition_' . $select_query->nextPlaceholder(); + $local_arguments[$placeholder_i] = $value; + } + + $operator = !empty($info['operator']) ? $info['operator'] : 'IN'; + $placeholder = '( ' . implode(', ', array_keys($local_arguments)) . ' )'; + $arguments += $local_arguments; + } + else { + // With a single value, the '=' operator is implicit. + $operator = !empty($info['operator']) ? $info['operator'] : '='; + $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder(); + } + // Set 'field' as join table field if available or set 'left field' as + // join table field is not set. + if (isset($info['field'])) { + $join_table_field = "$join_table$info[field]"; + // Allow the value to be set either with the 'value' element or + // with 'left_field'. + if (isset($info['left_field'])) { + $placeholder = "$left[alias].$info[left_field]"; + } + else { + $arguments[$placeholder] = $info['value']; + } + } + // Set 'left field' as join table field is not set. + else { + $join_table_field = "$left[alias].$info[left_field]"; + $arguments[$placeholder] = $info['value']; + } + $extras[] = "$join_table_field $operator $placeholder"; + } + + $language_bundle_conditions = []; + foreach ($extras as $key => &$extra){ + if (strpos($extra, '.langcode') !== FALSE || strpos($extra, '.bundle') !== FALSE ) { + $language_bundle_conditions[] = $extra; + unset($extras[$key]); + } + } + + if ($extras) { + if (count($extras) == 1) { + $condition .= ' AND ' . array_shift($extras); + } + else { + $condition .= ' AND (' . implode(' ' . $this->extraOperator . ' ', $extras) . ')'; + } + } + if (!empty($language_bundle_conditions)) { + $condition .= ' AND (' . implode(' OR ', $language_bundle_conditions) . ')'; + // The array argument is removed because of a suspected bug. + foreach ($arguments as $key => $arg) { + if (is_array($arg)) { + unset($arguments[$key]); + } + } + } + } + elseif ($this->extra && is_string($this->extra)) { + $condition .= " AND ($this->extra)"; + } + } + + $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments); + } +} diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc index ea865e2..ed872d1 100644 --- a/core/modules/views/views.views.inc +++ b/core/modules/views/views.views.inc @@ -8,6 +8,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\Sql\SqlContentEntityStorage; +use Drupal\field\Entity\FieldConfig; use Drupal\field\FieldConfigInterface; use Drupal\field\FieldStorageConfigInterface; use Drupal\system\ActionConfigEntityInterface; @@ -338,6 +339,38 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora ); } + // Determine if the field instances are translatable. + $bundles_names = $field_storage->getBundles(); + $translation_join_type = FALSE; + $field_configs = []; + $translatable_configs = []; + $untranslatable_configs = []; + $untranslatable_config_bundles = []; + + foreach ($bundles_names as $bundle) { + $field_configs[$bundle] = FieldConfig::loadByName($entity_type->id(), $bundle, $field_name); + } + foreach ($field_configs as $bundle => $config) { + if ($config->isTranslatable()) { + $translatable_configs[$bundle] = $config; + } + else { + $untranslatable_configs[$bundle] = $config; + } + } + + // If the field is translatable on all the bundles, there will be a join on the langcode. + if (!empty($translatable_configs) && empty($untranslatable_configs)) { + $translation_join_type = 'language'; + } + // If the field is translatable only on certain bundles, there will be a join on langcode OR bundle name. + elseif (!empty($translatable_configs) && !empty($untranslatable_configs)) { + foreach ($untranslatable_configs as $config) { + $untranslatable_config_bundles[] = $config->getTargetBundle(); + } + $translation_join_type = 'language_bundle'; + } + // Build the relationships between the field table and the entity tables. $table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_CURRENT]['alias']; if ($data_table) { @@ -347,7 +380,6 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora 'field' => 'entity_id', 'extra' => array( array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE), - array('left_field' => 'langcode', 'field' => 'langcode'), ), ); } @@ -362,6 +394,24 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora ); } + if ($translation_join_type === 'language_bundle') { + $data[$table_alias]['table']['join'][$data_table]['join_id'] = 'field_or_language_join'; + $data[$table_alias]['table']['join'][$data_table]['extra'][] = array( + 'left_field' => 'langcode', + 'field' => 'langcode', + ); + $data[$table_alias]['table']['join'][$data_table]['extra'][] = array( + 'value' => $untranslatable_config_bundles, + 'field' => 'bundle', + ); + } + elseif ($translation_join_type === 'language') { + $data[$table_alias]['table']['join'][$data_table]['extra'][] = array( + 'left_field' => 'langcode', + 'field' => 'langcode', + ); + } + if ($supports_revisions) { $table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias']; if ($entity_revision_data_table) { @@ -371,7 +421,6 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora 'field' => 'revision_id', 'extra' => array( array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE), - array('left_field' => 'langcode', 'field' => 'langcode'), ), ); } @@ -385,11 +434,26 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora ), ); } + if ($translation_join_type === 'language_bundle') { + $data[$table_alias]['table']['join'][$entity_revision_data_table]['join_id'] = 'field_or_language_join'; + $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = array( + 'left_field' => 'langcode', + 'field' => 'langcode', + ); + $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = array( + 'value' => $untranslatable_config_bundles, + 'field' => 'bundle', + ); + } + elseif ($translation_join_type === 'language') { + $data[$table_alias]['table']['join'][$entity_revision_data_table]['extra'][] = array( + 'left_field' => 'langcode', + 'field' => 'langcode', + ); + } } $group_name = $entity_type->getLabel(); - // Get the list of bundles the field appears in. - $bundles_names = $field_storage->getBundles(); // Build the list of additional fields to add to queries. $add_fields = array('delta', 'langcode', 'bundle'); foreach (array_keys($field_columns) as $column) {