diff --git a/core/modules/jsonapi/jsonapi.services.yml b/core/modules/jsonapi/jsonapi.services.yml index 21599d4ce3..9d6c0ef0b9 100644 --- a/core/modules/jsonapi/jsonapi.services.yml +++ b/core/modules/jsonapi/jsonapi.services.yml @@ -188,6 +188,7 @@ services: - '@jsonapi.serializer' - '@datetime.time' - '@current_user' + - '@language_manager' jsonapi.file_upload: class: Drupal\jsonapi\Controller\FileUpload arguments: diff --git a/core/modules/jsonapi/src/Controller/EntityResource.php b/core/modules/jsonapi/src/Controller/EntityResource.php index 7f03511e47..3abcc594a7 100644 --- a/core/modules/jsonapi/src/Controller/EntityResource.php +++ b/core/modules/jsonapi/src/Controller/EntityResource.php @@ -22,6 +22,8 @@ use Drupal\Core\Entity\RevisionLogInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemListInterface; +use Drupal\Core\Language\LanguageInterface; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Render\RenderContext; use Drupal\Core\Render\RendererInterface; use Drupal\Core\Session\AccountInterface; @@ -151,6 +153,13 @@ class EntityResource { */ protected $user; + /** + * The language manager. + * + * @var \Drupal\Core\Language\LanguageManagerInterface + */ + protected $languageManager; + /** * Instantiates an EntityResource object. * @@ -176,8 +185,10 @@ class EntityResource { * The time service. * @param \Drupal\Core\Session\AccountInterface $user * The current user account. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $field_manager, ResourceTypeRepositoryInterface $resource_type_repository, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, IncludeResolver $include_resolver, EntityAccessChecker $entity_access_checker, FieldResolver $field_resolver, SerializerInterface $serializer, TimeInterface $time, AccountInterface $user) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $field_manager, ResourceTypeRepositoryInterface $resource_type_repository, RendererInterface $renderer, EntityRepositoryInterface $entity_repository, IncludeResolver $include_resolver, EntityAccessChecker $entity_access_checker, FieldResolver $field_resolver, SerializerInterface $serializer, TimeInterface $time, AccountInterface $user, LanguageManagerInterface $language_manager) { $this->entityTypeManager = $entity_type_manager; $this->fieldManager = $field_manager; $this->resourceTypeRepository = $resource_type_repository; @@ -189,6 +200,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Ent $this->serializer = $serializer; $this->time = $time; $this->user = $user; + $this->languageManager = $language_manager; } /** @@ -884,7 +896,7 @@ protected function getCollectionQuery(ResourceType $resource_type, array $params foreach ($sort->fields() as $field) { $path = $this->fieldResolver->resolveInternalEntityQueryPath($resource_type, $field[Sort::PATH_KEY]); $direction = isset($field[Sort::DIRECTION_KEY]) ? $field[Sort::DIRECTION_KEY] : 'ASC'; - $langcode = isset($field[Sort::LANGUAGE_KEY]) ? $field[Sort::LANGUAGE_KEY] : NULL; + $langcode = isset($field[Sort::LANGUAGE_KEY]) ? $field[Sort::LANGUAGE_KEY] : $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); $query->sort($path, $direction, $langcode); } } diff --git a/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php index abe0a73d69..80d07f4dc8 100644 --- a/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php +++ b/core/modules/jsonapi/tests/src/Functional/JsonApiFunctionalMultilingualTest.php @@ -57,14 +57,13 @@ protected function setUp(): void { ]) ->setThirdPartySetting('content_translation', 'enabled', TRUE) ->save(); - - $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); } /** * Tests reading multilingual content. */ public function testReadMultilingual() { + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); // Different databases have different sort orders, so a sort is required so // test expectations do not need to vary per database. $default_sort = ['sort' => 'drupal_internal__nid']; @@ -98,6 +97,7 @@ public function testReadMultilingual() { * Tests updating a translation. */ public function testPatchTranslation() { + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); $node = $this->nodes[0]; $uuid = $node->uuid(); @@ -197,6 +197,7 @@ public function testPatchTranslation() { * Tests updating a translation fallback. */ public function testPatchTranslationFallback() { + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); $node = $this->nodes[0]; $uuid = $node->uuid(); @@ -238,6 +239,7 @@ public function testPatchTranslationFallback() { * Tests creating a translation. */ public function testPostTranslation() { + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ 'bypass node access', @@ -302,6 +304,7 @@ public function testPostTranslation() { * Tests deleting multilingual content. */ public function testDeleteMultilingual() { + $this->createDefaultContent(5, 5, TRUE, TRUE, static::IS_MULTILINGUAL, FALSE); $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE); $this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), [ 'bypass node access', @@ -322,4 +325,59 @@ public function testDeleteMultilingual() { $this->assertNull(Node::load($this->nodes[0]->id())); } + /** + * Tests multilingual JSON:API calls. + */ + public function testMultilingualGet() { + $titles = [ + [ + 'en' => 'Apple', + 'ca' => 'Z-Apple', + ], + [ + 'en' => 'Blackberry', + 'ca' => 'Y-Blackberry', + ], + [ + 'en' => 'Google', + 'ca' => 'X-Google', + ], + [ + 'en' => 'Motorola', + 'ca' => 'W-Motorola', + ], + ]; + + $expected_english_order = ['Apple', 'Blackberry', 'Google', 'Motorola']; + $expected_ca_order = ['W-Motorola', 'X-Google', 'Y-Blackberry', 'Z-Apple']; + foreach ($titles as $title) { + $node = Node::create(['title' => $title['en'], 'type' => 'article', 'langcode' => 'en']); + $node->addTranslation('ca', ['title' => $title['ca']]); + $node->save(); + } + + $output = Json::decode($this->drupalGet('/jsonapi/node/article', [ + 'query' => [ + 'sort' => 'title', + ], + ])); + $output_titles = array_map(function ($result) { + return $result['attributes']['title']; + }, $output['data']); + $this->assertCount(4, $output_titles); + $this->assertSame($expected_english_order, $output_titles); + + // Check the nodes with the langcode url. + $output = Json::decode($this->drupalGet('/ca/jsonapi/node/article', [ + 'query' => [ + 'sort' => 'title', + ], + ])); + $output_titles = array_map(function ($result) { + return $result['attributes']['title']; + }, $output['data']); + $this->assertCount(4, $output_titles); + $this->assertSame($expected_ca_order, $output_titles); + } + }