Skip to content

Commit a804f1d

Browse files
authored
Fix language handling and add language switch links (#509)
1 parent f26ecc3 commit a804f1d

47 files changed

Lines changed: 812 additions & 148 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

modules/graphql_core/src/Plugin/GraphQL/Enums/Languages/LanguageId.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,4 @@ public function buildValues(PluggableSchemaBuilderInterface $schemaBuilder) {
7070

7171
return $values;
7272
}
73-
7473
}

modules/graphql_core/src/Plugin/GraphQL/Fields/Breadcrumbs/Breadcrumbs.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
/**
1515
* Retrieve the breadcrumbs.
1616
*
17-
* TODO: Move this to `InternalUrl` (breaking change).
18-
*
1917
* @GraphQLField(
2018
* id = "breadcrumb",
2119
* secure = true,
2220
* name = "breadcrumb",
2321
* type = "[Link]",
24-
* parents = {"Url"},
22+
* parents = {"InternalUrl"},
2523
* )
2624
*/
2725
class Breadcrumbs extends FieldPluginBase implements ContainerFactoryPluginInterface {

modules/graphql_core/src/Plugin/GraphQL/Fields/Entity/EntityTranslation.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Drupal\Core\Entity\EntityInterface;
77
use Drupal\Core\Entity\EntityRepositoryInterface;
88
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
9+
use Drupal\Core\TypedData\TranslatableInterface;
910
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
1011
use Symfony\Component\DependencyInjection\ContainerInterface;
1112
use Youshido\GraphQL\Execution\ResolveInfo;
@@ -65,7 +66,7 @@ public function __construct(array $configuration, $pluginId, $pluginDefinition,
6566
* {@inheritdoc}
6667
*/
6768
public function resolveValues($value, array $args, ResolveInfo $info) {
68-
if ($value instanceof EntityInterface) {
69+
if ($value instanceof EntityInterface && $value instanceof TranslatableInterface && $value->isTranslatable()) {
6970
yield $this->entityRepository->getTranslationFromContext($value, $args['language']);
7071
}
7172
}

modules/graphql_core/src/Plugin/GraphQL/Fields/Entity/EntityTranslations.php

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Entity;
44

5+
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
56
use Drupal\Core\Entity\ContentEntityInterface;
7+
use Drupal\Core\Entity\EntityRepositoryInterface;
8+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
9+
use Drupal\Core\TypedData\TranslatableInterface;
610
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
11+
use Symfony\Component\DependencyInjection\ContainerInterface;
712
use Youshido\GraphQL\Execution\ResolveInfo;
813

914
/**
@@ -15,16 +20,58 @@
1520
* parents = {"Entity"}
1621
* )
1722
*/
18-
class EntityTranslations extends FieldPluginBase {
23+
class EntityTranslations extends FieldPluginBase implements ContainerFactoryPluginInterface {
24+
use DependencySerializationTrait;
25+
26+
/**
27+
* The entity repository service.
28+
*
29+
* @var \Drupal\Core\Entity\EntityRepositoryInterface
30+
*/
31+
protected $entityRepository;
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
37+
return new static(
38+
$configuration,
39+
$pluginId,
40+
$pluginDefinition,
41+
$container->get('entity.repository')
42+
);
43+
}
44+
45+
/**
46+
* EntityTranslations constructor.
47+
*
48+
* @param array $configuration
49+
* The plugin configuration array.
50+
* @param string $pluginId
51+
* The plugin id.
52+
* @param mixed $pluginDefinition
53+
* The plugin definition array.
54+
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository
55+
* The entity repository service.
56+
*/
57+
public function __construct(
58+
array $configuration,
59+
$pluginId,
60+
$pluginDefinition,
61+
EntityRepositoryInterface $entityRepository
62+
) {
63+
parent::__construct($configuration, $pluginId, $pluginDefinition);
64+
$this->entityRepository = $entityRepository;
65+
}
1966

2067
/**
2168
* {@inheritdoc}
2269
*/
2370
public function resolveValues($value, array $args, ResolveInfo $info) {
24-
if ($value instanceof ContentEntityInterface) {
71+
if ($value instanceof ContentEntityInterface && $value instanceof TranslatableInterface && $value->isTranslatable()) {
2572
$languages = $value->getTranslationLanguages();
2673
foreach ($languages as $language) {
27-
yield $value->getTranslation($language->getId());
74+
yield $this->entityRepository->getTranslationFromContext($value, $language->getId());
2875
}
2976
}
3077
}

modules/graphql_core/src/Plugin/GraphQL/Fields/Links/LinkAttribute.php renamed to modules/graphql_core/src/Plugin/GraphQL/Fields/Entity/Fields/Link/LinkAttribute.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Links;
3+
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Entity\Fields\Link;
44

55
use Drupal\Component\Utility\NestedArray;
66
use Drupal\link\LinkItemInterface;

modules/graphql_core/src/Plugin/GraphQL/Fields/Links/LinkUrl.php renamed to modules/graphql_core/src/Plugin/GraphQL/Fields/Entity/Fields/Link/LinkUrl.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Links;
3+
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\Entity\Fields\Link;
44

55
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
66
use Drupal\link\LinkItemInterface;

modules/graphql_core/src/Plugin/GraphQL/Fields/EntityQuery/EntityQuery.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public function resolveValues($value, array $args, ResolveInfo $info) {
101101
* Retrieve the target entity type of this plugin.
102102
*
103103
* @param mixed $value
104-
* The parent entity type.
104+
* The parent value.
105105
* @param array $args
106106
* The field arguments array.
107107
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
@@ -174,9 +174,33 @@ protected function getBaseQuery($value, array $args, ResolveInfo $info) {
174174
$query = $entityStorage->getQuery();
175175
$query->accessCheck(TRUE);
176176

177+
// The context object can e.g. transport the parent entity language.
178+
$query->addMetaData('graphql_context', $this->getQueryContext($value, $args, $info));
179+
177180
return $query;
178181
}
179182

183+
/**
184+
* Retrieves an arbitrary value to write into the query metadata.
185+
*
186+
* @param mixed $value
187+
* The parent value.
188+
* @param array $args
189+
* The field arguments array.
190+
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
191+
* The resolve info object.
192+
*
193+
* @return mixed
194+
* The query context.
195+
*/
196+
protected function getQueryContext($value, array $args, ResolveInfo $info) {
197+
// Forward the whole set of arguments by default.
198+
return [
199+
'parent' => $value,
200+
'args' => $args,
201+
'info' => $info,
202+
];
203+
}
180204

181205
/**
182206
* Apply the specified revision filtering mode to the query.

modules/graphql_core/src/Plugin/GraphQL/Fields/EntityQuery/EntityQueryEntities.php

Lines changed: 80 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\EntityQuery;
44

55
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
6+
use Drupal\Core\Entity\ContentEntityInterface;
7+
use Drupal\Core\Entity\EntityInterface;
8+
use Drupal\Core\Entity\EntityRepositoryInterface;
69
use Drupal\Core\Entity\EntityTypeManagerInterface;
710
use Drupal\Core\Entity\Query\QueryInterface;
811
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12+
use Drupal\Core\TypedData\TranslatableInterface;
913
use Drupal\graphql\GraphQL\Buffers\EntityBuffer;
1014
use Drupal\graphql\GraphQL\Cache\CacheableValue;
1115
use Drupal\graphql\Plugin\GraphQL\Fields\FieldPluginBase;
@@ -20,7 +24,10 @@
2024
* secure = true,
2125
* name = "entities",
2226
* type = "[Entity]",
23-
* parents = {"EntityQueryResult"}
27+
* parents = {"EntityQueryResult"},
28+
* arguments = {
29+
* "language" = "LanguageId"
30+
* }
2431
* )
2532
*/
2633
class EntityQueryEntities extends FieldPluginBase implements ContainerFactoryPluginInterface {
@@ -40,6 +47,13 @@ class EntityQueryEntities extends FieldPluginBase implements ContainerFactoryPlu
4047
*/
4148
protected $entityTypeManager;
4249

50+
/**
51+
* The entity repository service.
52+
*
53+
* @var \Drupal\Core\Entity\EntityRepositoryInterface
54+
*/
55+
protected $entityRepository;
56+
4357
/**
4458
* {@inheritdoc}
4559
*/
@@ -49,6 +63,7 @@ public static function create(ContainerInterface $container, array $configuratio
4963
$pluginId,
5064
$pluginDefinition,
5165
$container->get('entity_type.manager'),
66+
$container->get('entity.repository'),
5267
$container->get('graphql.buffer.entity')
5368
);
5469
}
@@ -64,6 +79,8 @@ public static function create(ContainerInterface $container, array $configuratio
6479
* The plugin definition array.
6580
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
6681
* The entity type manager service.
82+
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository
83+
* The entity repository service.
6784
* @param \Drupal\graphql\GraphQL\Buffers\EntityBuffer $entityBuffer
6885
* The entity buffer service.
6986
*/
@@ -72,11 +89,13 @@ public function __construct(
7289
$pluginId,
7390
$pluginDefinition,
7491
EntityTypeManagerInterface $entityTypeManager,
92+
EntityRepositoryInterface $entityRepository,
7593
EntityBuffer $entityBuffer
7694
) {
7795
parent::__construct($configuration, $pluginId, $pluginDefinition);
78-
$this->entityBuffer = $entityBuffer;
7996
$this->entityTypeManager = $entityTypeManager;
97+
$this->entityRepository = $entityRepository;
98+
$this->entityBuffer = $entityBuffer;
8099
}
81100

82101
/**
@@ -86,13 +105,14 @@ public function resolveValues($value, array $args, ResolveInfo $info) {
86105
if ($value instanceof QueryInterface) {
87106
$type = $value->getEntityTypeId();
88107
$result = $value->execute();
108+
$context = $value->getMetaData('graphql_context');
89109

90110
if ($value->hasTag('revisions')) {
91-
return $this->resolveFromRevisionIds($type, array_keys($result));
111+
return $this->resolveFromRevisionIds($type, array_keys($result), $context, $args, $info);
92112
}
93113

94114
// If this is a revision query, the version ids are the array keys.
95-
return $this->resolveFromEntityIds($type, array_values($result));
115+
return $this->resolveFromEntityIds($type, array_values($result), $context, $args, $info);
96116
}
97117
}
98118

@@ -103,14 +123,20 @@ public function resolveValues($value, array $args, ResolveInfo $info) {
103123
* The entity type.
104124
* @param array $ids
105125
* The entity ids to load.
126+
* @param mixed $context
127+
* The query context.
128+
* @param array $args
129+
* The field arguments array.
130+
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
131+
* The resolve info object.
106132
*
107133
* @return \Closure
108134
* The deferred resolver.
109135
*/
110-
protected function resolveFromEntityIds($type, $ids) {
136+
protected function resolveFromEntityIds($type, $ids, $context, array $args, ResolveInfo $info) {
111137
$resolve = $this->entityBuffer->add($type, $ids);
112-
return function($value, array $args, ResolveInfo $info) use ($resolve) {
113-
return $this->resolveEntities($resolve());
138+
return function($value, array $args, ResolveInfo $info) use ($resolve, $context) {
139+
return $this->resolveEntities($resolve(), $context, $args, $info);
114140
};
115141
}
116142

@@ -121,33 +147,51 @@ protected function resolveFromEntityIds($type, $ids) {
121147
* The entity type.
122148
* @param array $ids
123149
* The entity revision ids to load.
150+
* @param mixed $context
151+
* The query context.
152+
* @param array $args
153+
* The field arguments array.
154+
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
155+
* The resolve info object.
124156
*
125157
* @return \Generator
126158
* The resolved revisions.
127159
*/
128-
protected function resolveFromRevisionIds($type, $ids) {
160+
protected function resolveFromRevisionIds($type, $ids, $context, array $args, ResolveInfo $info) {
129161
$storage = $this->entityTypeManager->getStorage($type);
130162
$entities = array_map(function ($id) use ($storage) {
131163
return $storage->loadRevision($id);
132164
}, $ids);
133165

134-
return $this->resolveEntities($entities);
166+
return $this->resolveEntities($entities, $context, $args, $info);
135167
}
136168

137169
/**
138170
* Resolves entity objects and checks view permissions.
139171
*
140172
* @param array $entities
141173
* The entities to resolve.
174+
* @param mixed $context
175+
* The query context.
176+
* @param array $args
177+
* The field arguments array.
178+
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
179+
* The resolve info object.
142180
*
143181
* @return \Generator
144182
* The resolved entities.
145183
*/
146-
protected function resolveEntities(array $entities) {
184+
protected function resolveEntities(array $entities, $context, array $args, ResolveInfo $info) {
185+
$language = $this->negotiateLanguage($context, $args, $info);
186+
147187
/** @var \Drupal\Core\Entity\EntityInterface $entity */
148188
foreach ($entities as $entity) {
149-
$access = $entity->access('view', NULL, TRUE);
189+
// Translate the entity if it is translatable and a language was given.
190+
if ($language && $entity instanceof TranslatableInterface && $entity->isTranslatable()) {
191+
$entity = $this->entityRepository->getTranslationFromContext($entity, $language);
192+
}
150193

194+
$access = $entity->access('view', NULL, TRUE);
151195
if ($access->isAllowed()) {
152196
yield $entity->addCacheableDependency($access);
153197
}
@@ -157,4 +201,29 @@ protected function resolveEntities(array $entities) {
157201
}
158202
}
159203

204+
/**
205+
* Negotiate the language for the resolved entities.
206+
*
207+
* @param mixed $context
208+
* The query context.
209+
* @param array $args
210+
* The field arguments array.
211+
* @param \Youshido\GraphQL\Execution\ResolveInfo $info
212+
* The resolve info object.
213+
*
214+
* @return string|null
215+
* The negotiated language id.
216+
*/
217+
protected function negotiateLanguage($context, $args, ResolveInfo $info) {
218+
if (!empty($args['language'])) {
219+
return $args['language'];
220+
}
221+
222+
if (isset($context['parent']) && ($parent = $context['parent']) && $parent instanceof EntityInterface) {
223+
return $parent->language()->getId();
224+
}
225+
226+
return NULL;
227+
}
228+
160229
}

modules/graphql_core/src/Plugin/GraphQL/Fields/EntityReference/EntityReferenceReverse.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Drupal\graphql_core\Plugin\GraphQL\Fields\EntityReference;
44

5-
use Drupal\Core\Entity\EntityInterface;
5+
use Drupal\Core\Entity\ContentEntityInterface;
66
use Drupal\graphql_core\Plugin\GraphQL\Fields\EntityQuery\EntityQuery;
77
use Youshido\GraphQL\Execution\ResolveInfo;
88

@@ -36,7 +36,7 @@ class EntityReferenceReverse extends EntityQuery {
3636
* {@inheritdoc}
3737
*/
3838
public function getBaseQuery($value, array $args, ResolveInfo $info) {
39-
if ($value instanceof EntityInterface) {
39+
if ($value instanceof ContentEntityInterface) {
4040
$query = parent::getBaseQuery($value, $args, $info);
4141

4242
// Add the target field condition to the query.

0 commit comments

Comments
 (0)