Issue #3065212 by amateescu, pmelab, leolando.tan: Workspaces views query alter multiplies result set by number of languages

merge-requests/1119/head
catch 2019-07-11 18:49:22 +01:00
parent 3c7bc9f908
commit 88fede1bcf
2 changed files with 39 additions and 7 deletions

View File

@ -6,6 +6,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\query\Sql; use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\Plugin\ViewsHandlerManager; use Drupal\views\Plugin\ViewsHandlerManager;
@ -55,6 +56,13 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
*/ */
protected $viewsJoinPluginManager; protected $viewsJoinPluginManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/** /**
* Constructs a new ViewsQueryAlter instance. * Constructs a new ViewsQueryAlter instance.
* *
@ -68,13 +76,16 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
* The views data. * The views data.
* @param \Drupal\views\Plugin\ViewsHandlerManager $views_join_plugin_manager * @param \Drupal\views\Plugin\ViewsHandlerManager $views_join_plugin_manager
* The views join plugin manager. * The views join plugin manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/ */
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, WorkspaceManagerInterface $workspace_manager, ViewsData $views_data, ViewsHandlerManager $views_join_plugin_manager) { public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, WorkspaceManagerInterface $workspace_manager, ViewsData $views_data, ViewsHandlerManager $views_join_plugin_manager, LanguageManagerInterface $language_manager) {
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager; $this->entityFieldManager = $entity_field_manager;
$this->workspaceManager = $workspace_manager; $this->workspaceManager = $workspace_manager;
$this->viewsData = $views_data; $this->viewsData = $views_data;
$this->viewsJoinPluginManager = $views_join_plugin_manager; $this->viewsJoinPluginManager = $views_join_plugin_manager;
$this->languageManager = $language_manager;
} }
/** /**
@ -86,7 +97,8 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
$container->get('entity_field.manager'), $container->get('entity_field.manager'),
$container->get('workspaces.manager'), $container->get('workspaces.manager'),
$container->get('views.views_data'), $container->get('views.views_data'),
$container->get('plugin.manager.views.join') $container->get('plugin.manager.views.join'),
$container->get('language_manager')
); );
} }
@ -335,7 +347,7 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
// to modify the join and make sure that 'workspace_association' comes // to modify the join and make sure that 'workspace_association' comes
// first. // first.
if (empty($table_queue[$alias]['join']->workspace_adjusted)) { if (empty($table_queue[$alias]['join']->workspace_adjusted)) {
$table_queue[$alias]['join'] = $this->getRevisionTableJoin($relationship, $base_revision_table, $revision_field, $workspace_association_table); $table_queue[$alias]['join'] = $this->getRevisionTableJoin($relationship, $base_revision_table, $revision_field, $workspace_association_table, $entity_type);
// We also have to ensure that our 'workspace_association' comes before // We also have to ensure that our 'workspace_association' comes before
// this. // this.
$this->moveEntityTable($query, $workspace_association_table, $alias); $this->moveEntityTable($query, $workspace_association_table, $alias);
@ -346,7 +358,7 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
} }
// Construct a new join. // Construct a new join.
$join = $this->getRevisionTableJoin($relationship, $base_revision_table, $revision_field, $workspace_association_table); $join = $this->getRevisionTableJoin($relationship, $base_revision_table, $revision_field, $workspace_association_table, $entity_type);
return $query->queueTable($base_revision_table, $relationship, $join); return $query->queueTable($base_revision_table, $relationship, $join);
} }
@ -362,19 +374,27 @@ class ViewsQueryAlter implements ContainerInjectionInterface {
* @param string $workspace_association_table * @param string $workspace_association_table
* The alias of the 'workspace_association' table joined to the main entity * The alias of the 'workspace_association' table joined to the main entity
* table. * table.
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type that is being queried.
* *
* @return \Drupal\views\Plugin\views\join\JoinPluginInterface * @return \Drupal\views\Plugin\views\join\JoinPluginInterface
* An adjusted views join object to add to the query. * An adjusted views join object to add to the query.
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/ */
protected function getRevisionTableJoin($relationship, $table, $field, $workspace_association_table) { protected function getRevisionTableJoin($relationship, $table, $field, $workspace_association_table, EntityTypeInterface $entity_type) {
$definition = [ $definition = [
'table' => $table, 'table' => $table,
'field' => $field, 'field' => $field,
// Making this explicitly null allows the left table to be a formula. // Making this explicitly NULL allows the left table to be a formula.
'left_table' => NULL, 'left_table' => NULL,
'left_field' => "COALESCE($workspace_association_table.target_entity_revision_id, $relationship.$field)", 'left_field' => "COALESCE($workspace_association_table.target_entity_revision_id, $relationship.$field)",
]; ];
if ($entity_type->isTranslatable() && $this->languageManager->isMultilingual()) {
$langcode_field = $entity_type->getKey('langcode');
$definition['extra'] = "$table.$langcode_field = $relationship.$langcode_field";
}
/** @var \Drupal\views\Plugin\views\join\JoinPluginInterface $join */ /** @var \Drupal\views\Plugin\views\join\JoinPluginInterface $join */
$join = $this->viewsJoinPluginManager->createInstance('standard', $definition); $join = $this->viewsJoinPluginManager->createInstance('standard', $definition);
$join->adjusted = TRUE; $join->adjusted = TRUE;

View File

@ -6,6 +6,7 @@ use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormState;
use Drupal\entity_test\Entity\EntityTestMulRevPub; use Drupal\entity_test\Entity\EntityTestMulRevPub;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\system\Form\SiteInformationForm; use Drupal\system\Form\SiteInformationForm;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait; use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
@ -62,6 +63,8 @@ class WorkspaceIntegrationTest extends KernelTestBase {
'user', 'user',
'system', 'system',
'views', 'views',
'language',
'content_translation',
]; ];
/** /**
@ -78,20 +81,29 @@ class WorkspaceIntegrationTest extends KernelTestBase {
$this->installEntitySchema('node'); $this->installEntitySchema('node');
$this->installEntitySchema('user'); $this->installEntitySchema('user');
$this->installConfig(['filter', 'node', 'system']); $this->installConfig(['filter', 'node', 'system', 'language', 'content_translation']);
$this->installSchema('system', ['key_value_expire', 'sequences']); $this->installSchema('system', ['key_value_expire', 'sequences']);
$this->installSchema('node', ['node_access']); $this->installSchema('node', ['node_access']);
$language = ConfigurableLanguage::createFromLangcode('de');
$language->save();
$this->createContentType(['type' => 'page']); $this->createContentType(['type' => 'page']);
$this->setCurrentUser($this->createUser(['administer nodes'])); $this->setCurrentUser($this->createUser(['administer nodes']));
$this->container->get('content_translation.manager')->setEnabled('node', 'page', TRUE);
// Create two nodes, a published and an unpublished one, so we can test the // Create two nodes, a published and an unpublished one, so we can test the
// behavior of the module with default/existing content. // behavior of the module with default/existing content.
$this->createdTimestamp = \Drupal::time()->getRequestTime(); $this->createdTimestamp = \Drupal::time()->getRequestTime();
$this->nodes[] = $this->createNode(['title' => 'live - 1 - r1 - published', 'created' => $this->createdTimestamp++, 'status' => TRUE]); $this->nodes[] = $this->createNode(['title' => 'live - 1 - r1 - published', 'created' => $this->createdTimestamp++, 'status' => TRUE]);
$this->nodes[] = $this->createNode(['title' => 'live - 2 - r2 - unpublished', 'created' => $this->createdTimestamp++, 'status' => FALSE]); $this->nodes[] = $this->createNode(['title' => 'live - 2 - r2 - unpublished', 'created' => $this->createdTimestamp++, 'status' => FALSE]);
$translation = $this->nodes[0]->addTranslation('de');
$translation->setTitle('live - 1 - r1 - published - de');
$translation->save();
} }
/** /**