diff --git a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php index bca8f68..071647a 100644 --- a/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/EntityRouteAlterSubscriber.php @@ -50,10 +50,30 @@ public function onRoutingRouteAlterSetType(RouteBuildEvent $event) { } /** + * Set the latest revision flag for entity forms. + * + * @param \Drupal\Core\Routing\RouteBuildEvent $event + * The event to process. + */ + public function onRoutingRouteAlterSetLatestRevision(RouteBuildEvent $event) { + foreach ($event->getRouteCollection() as $route) { + if (!$route->getDefault('_entity_form')) { + continue; + } + $parameters = $route->getOption('parameters') ?: []; + foreach ($parameters as &$parameter) { + $parameter['load_latest_revision'] = TRUE; + } + $route->setOption('parameters', $parameters); + } + } + + /** * {@inheritdoc} */ public static function getSubscribedEvents() { $events[RoutingEvents::ALTER][] = ['onRoutingRouteAlterSetType', -150]; + $events[RoutingEvents::ALTER][] = ['onRoutingRouteAlterSetLatestRevision', -149]; return $events; } diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php index 67f6a89..5478de8 100644 --- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php +++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php @@ -62,6 +62,25 @@ public function convert($value, $definition, $name, array $defaults) { $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults); if ($storage = $this->entityManager->getStorage($entity_type_id)) { $entity = $storage->load($value); + + // If the entity type is revisionable, load the latest revision. + if ($entity instanceof EntityInterface && !empty($definition['load_latest_revision']) && $entity->getEntityType()->isRevisionable()) { + // @todo, replace this with query with a standardised way of getting the + // latest revision in https://www.drupal.org/node/2784201. + $entity_revisions = $storage + ->getQuery() + ->allRevisions() + ->condition($entity->getEntityType()->getKey('id'), $entity->id()) + ->sort($entity->getEntityType()->getKey('revision'), 'DESC') + ->range(0, 1) + ->execute(); + $revision_ids = array_keys($entity_revisions); + $latest_revision_id = array_shift($revision_ids); + if ($entity->getRevisionId() != $latest_revision_id) { + $entity = $storage->loadRevision($latest_revision_id); + } + } + // If the entity type is translatable, ensure we return the proper // translation object for the current context. if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) { diff --git a/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php b/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php new file mode 100644 index 0000000..9f14d44 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/ParamConverter/EntityConverterLatestRevisionTest.php @@ -0,0 +1,125 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test_mulrev'); + $this->installConfig(['system', 'language']); + + $this->converter = $this->container->get('paramconverter.entity'); + + ConfigurableLanguage::createFromLangcode('de')->save(); + } + + /** + * Test with no matching entity. + */ + public function testNoEntity() { + $converted = $this->converter->convert(1, [ + 'load_latest_revision' => TRUE, + 'type' => 'entity:entity_test_mulrev', + ], 'foo', []); + $this->assertEquals(NULL, $converted); + } + + /** + * Test with no pending revision. + */ + public function testEntityNoPendingRevision() { + $entity = EntityTestMulRev::create(); + $entity->save(); + + $converted = $this->converter->convert(1, [ + 'load_latest_revision' => TRUE, + 'type' => 'entity:entity_test_mulrev', + ], 'foo', []); + $this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId()); + } + + /** + * Test with a pending revision. + */ + public function testEntityWithPendingRevision() { + $entity = EntityTestMulRev::create(); + $entity->save(); + + $entity->isDefaultRevision(FALSE); + $entity->setNewRevision(TRUE); + $entity->save(); + + $converted = $this->converter->convert(1, [ + 'load_latest_revision' => TRUE, + 'type' => 'entity:entity_test_mulrev', + ], 'foo', []); + + $this->assertEquals($entity->getLoadedRevisionId(), $converted->getLoadedRevisionId()); + } + + /** + * Test with a translated pending revision. + */ + public function testWithTranslatedPendingRevision() { + $entity = EntityTestMulRev::create(); + $entity->save(); + + // Create a translated pending revision. + $translated_entity = $entity->addTranslation('de'); + $translated_entity->isDefaultRevision(FALSE); + $translated_entity->setNewRevision(TRUE); + $translated_entity->save(); + + // Change the site language so the converters will attempt to load entities + // with 'de'. + $this->config('system.site')->set('default_langcode', 'de')->save(); + + // The default loaded language is still 'en'. + EntityTestMulRev::load($entity->id()); + $this->assertEquals('en', $entity->language()->getId()); + + // The converter will load the latest revision in the correct language. + $converted = $this->converter->convert(1, [ + 'load_latest_revision' => TRUE, + 'type' => 'entity:entity_test_mulrev', + ], 'foo', []); + $this->assertEquals('de', $converted->language()->getId()); + $this->assertEquals($translated_entity->getLoadedRevisionId(), $converted->getLoadedRevisionId()); + } + +}