Skip to content

Commit 69270aa

Browse files
committed
Issue #3371301 by lauriii, srishtiiee, longwave, smustgrave: Retrieve field type category information in \Drupal\Core\Field\FieldTypePluginManager::getGroupedDefinitions
1 parent adce845 commit 69270aa

7 files changed

+228
-16
lines changed

core.field_type_categories.yml

+1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ date_time:
1515
description: 'Field to store date and time values.'
1616
weight: -10
1717
general:
18+
label: 'General'
1819
class: \Drupal\Core\Field\FallbackFieldTypeCategory

lib/Drupal/Core/Field/FieldTypeCategoryManager.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected function getDiscovery(): YamlDiscovery {
7979
* {@inheritdoc}
8080
*/
8181
public function getFallbackPluginId($plugin_id, array $configuration = []): string {
82-
return 'general';
82+
return FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
8383
}
8484

8585
}

lib/Drupal/Core/Field/FieldTypeCategoryManagerInterface.php

+6
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@
88
* Defines an interface for field type category managers.
99
*/
1010
interface FieldTypeCategoryManagerInterface extends PluginManagerInterface {
11+
12+
/**
13+
* Fallback category for field types.
14+
*/
15+
const FALLBACK_CATEGORY = 'general';
16+
1117
}

lib/Drupal/Core/Field/FieldTypePluginManager.php

+32-12
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818
*/
1919
class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
2020

21-
/**
22-
* Default category for field types.
23-
*/
24-
const DEFAULT_CATEGORY = 'general';
25-
2621
use CategorizingPluginManagerTrait {
2722
getGroupedDefinitions as protected getGroupedDefinitionsTrait;
2823
}
@@ -107,11 +102,11 @@ public function processDefinition(&$definition, $plugin_id) {
107102

108103
if ($definition['category'] instanceof TranslatableMarkup) {
109104
@trigger_error('Using a translatable string as a category for field type is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3364271', E_USER_DEPRECATED);
110-
$definition['category'] = static::DEFAULT_CATEGORY;
105+
$definition['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
111106
}
112107
elseif (empty($definition['category'])) {
113108
// Ensure that every field type has a category.
114-
$definition['category'] = static::DEFAULT_CATEGORY;
109+
$definition['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
115110
}
116111
}
117112

@@ -164,20 +159,45 @@ public function getFieldSettingsSummary(FieldDefinitionInterface $field_definiti
164159
}
165160

166161
/**
167-
* {@inheritdoc}
162+
* Gets sorted field type definitions grouped by category.
163+
*
164+
* In addition to grouping, both categories and its entries are sorted,
165+
* whereas plugin definitions are sorted by label.
166+
*
167+
* @param array[]|null $definitions
168+
* (optional) The plugin definitions to group. If omitted, all plugin
169+
* definitions are used.
170+
* @param string $label_key
171+
* (optional) The array key to use as the label of the field type.
172+
* @param string $category_label_key
173+
* (optional) The array key to use as the label of the category.
174+
*
175+
* @return array[]
176+
* Keys are category names, and values are arrays of which the keys are
177+
* plugin IDs and the values are plugin definitions.
168178
*/
169-
public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
179+
public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label', $category_label_key = 'label') {
170180
$grouped_categories = $this->getGroupedDefinitionsTrait($definitions, $label_key);
171181
$category_info = $this->fieldTypeCategoryManager->getDefinitions();
182+
183+
// Ensure that all the referenced categories exist.
172184
foreach ($grouped_categories as $group => $definitions) {
173-
if (!isset($category_info[$group]) && $group !== static::DEFAULT_CATEGORY) {
185+
if (!isset($category_info[$group])) {
174186
assert(FALSE, "\"$group\" must be defined in MODULE_NAME.field_type_categories.yml");
175-
$grouped_categories[static::DEFAULT_CATEGORY] += $definitions;
187+
if (!isset($grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY])) {
188+
$grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY] = [];
189+
}
190+
$grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY] += $definitions;
176191
unset($grouped_categories[$group]);
177192
}
178193
}
179194

180-
return $grouped_categories;
195+
$normalized_grouped_categories = [];
196+
foreach ($grouped_categories as $group => $definitions) {
197+
$normalized_grouped_categories[(string) $category_info[$group][$category_label_key]] = $definitions;
198+
}
199+
200+
return $normalized_grouped_categories;
181201
}
182202

183203
/**

modules/field_ui/src/Form/FieldStorageAddForm.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
156156
];
157157

158158
$field_type_options = $unique_definitions = [];
159-
$grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions());
159+
$grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions(), 'label', 'id');
160160
// Invoke a hook to get category properties.
161161
foreach ($grouped_definitions as $category => $field_types) {
162162
foreach ($field_types as $name => $field_type) {
@@ -165,6 +165,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
165165
$category_plugin = $this->fieldTypeCategoryManager->createInstance($category, $unique_definitions[$category][$name]);
166166
$field_type_options[$category_plugin->getPluginId()] = ['unique_identifier' => $name] + $field_type;
167167
}
168+
else {
169+
$field_type_options[(string) $field_type['label']] = ['unique_identifier' => $name] + $field_type;
170+
}
168171
}
169172
}
170173
$form['add-label'] = [

modules/media/media.module

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use Drupal\Component\Plugin\DerivativeInspectionInterface;
99
use Drupal\Core\Access\AccessResult;
1010
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
1111
use Drupal\Core\Entity\EntityInterface;
12-
use Drupal\Core\Field\FieldTypePluginManager;
12+
use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
1313
use Drupal\Core\Form\FormStateInterface;
1414
use Drupal\Core\Render\Element;
1515
use Drupal\Core\Render\Element\RenderElement;
@@ -187,7 +187,7 @@ function media_field_ui_preconfigured_options_alter(array &$options, $field_type
187187
if (!empty($options['media'])) {
188188
$options['media']['description'] = t('Field to reference media. Allows uploading and selecting from uploaded media.');
189189
$options['media']['weight'] = -25;
190-
$options['media']['category'] = FieldTypePluginManager::DEFAULT_CATEGORY;
190+
$options['media']['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
191191
$options['media']['entity_view_display']['type'] = 'entity_reference_entity_view';
192192
}
193193
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
3+
namespace Drupal\Tests\Core\Field;
4+
5+
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
6+
use Drupal\Core\Cache\CacheBackendInterface;
7+
use Drupal\Core\DependencyInjection\ContainerBuilder;
8+
use Drupal\Core\Extension\ModuleHandlerInterface;
9+
use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
10+
use Drupal\Core\Field\FieldTypePluginManager;
11+
use Drupal\Core\Session\AccountInterface;
12+
use Drupal\Core\TypedData\TypedDataManager;
13+
use Drupal\Tests\UnitTestCase;
14+
use Prophecy\Argument;
15+
16+
/**
17+
* @coversDefaultClass \Drupal\Core\Field\FieldTypePluginManager
18+
* @group Field
19+
*/
20+
class FieldTypePluginManagerTest extends UnitTestCase {
21+
22+
/**
23+
* The field type plugin manager.
24+
*
25+
* @var \Drupal\Core\Field\FieldTypePluginManager
26+
*/
27+
protected FieldTypePluginManager $fieldTypeManager;
28+
29+
/**
30+
* A mocked module handler.
31+
*
32+
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
33+
*/
34+
protected $moduleHandler;
35+
36+
/**
37+
* A mocked module handler.
38+
*
39+
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
40+
*/
41+
protected $fieldTypeCategoryManager;
42+
43+
/**
44+
* A mocked plugin discovery.
45+
*
46+
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\Prophecy\Prophecy\ObjectProphecy
47+
*/
48+
protected $discovery;
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
protected function setUp(): void {
54+
parent::setUp();
55+
56+
$container = new ContainerBuilder();
57+
$current_user = $this->prophesize(AccountInterface::class);
58+
$container->set('current_user', $current_user->reveal());
59+
$container->set('string_translation', $this->getStringTranslationStub());
60+
\Drupal::setContainer($container);
61+
62+
$cache_backend = $this->prophesize(CacheBackendInterface::class);
63+
$this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
64+
$this->moduleHandler->alter('field_info', Argument::any())->willReturn(NULL);
65+
$typed_data_manager = $this->prophesize(TypedDataManager::class);
66+
$this->fieldTypeCategoryManager = $this->prophesize(FieldTypeCategoryManagerInterface::class);
67+
68+
$this->fieldTypeManager = new FieldTypePluginManager(new \ArrayObject(), $cache_backend->reveal(), $this->moduleHandler->reveal(), $typed_data_manager->reveal(), $this->fieldTypeCategoryManager->reveal());
69+
$this->fieldTypeManager->setStringTranslation($this->getStringTranslationStub());
70+
71+
$this->discovery = $this->prophesize(DiscoveryInterface::class);
72+
$property = new \ReflectionProperty(FieldTypePluginManager::class, 'discovery');
73+
$property->setAccessible(TRUE);
74+
$property->setValue($this->fieldTypeManager, $this->discovery->reveal());
75+
}
76+
77+
/**
78+
* @covers ::getGroupedDefinitions
79+
*/
80+
public function testGetGroupedDefinitions() {
81+
$this->discovery->getDefinitions()->willReturn([
82+
'telephone' => [
83+
'category' => 'general',
84+
'label' => 'Telephone',
85+
'id' => 'telephone',
86+
],
87+
'string' => [
88+
'category' => 'text',
89+
'label' => 'Text (plain)',
90+
'id' => 'string',
91+
],
92+
'integer' => [
93+
'category' => 'number',
94+
'label' => 'Number (integer)',
95+
'id' => 'integer',
96+
],
97+
'float' => [
98+
'id' => 'float',
99+
'label' => 'Number (float)',
100+
'category' => 'number',
101+
],
102+
]);
103+
104+
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([
105+
'general' => [
106+
'label' => 'General',
107+
'id' => 'general',
108+
],
109+
'number' => [
110+
'label' => 'Number 🦥',
111+
'id' => 'number',
112+
],
113+
'text' => [
114+
'label' => 'Text 🐈',
115+
'id' => 'text',
116+
],
117+
'empty_group' => [
118+
'label' => 'Empty 🦗',
119+
'id' => 'empty_group',
120+
],
121+
]);
122+
123+
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions();
124+
$this->assertEquals(['General', 'Number 🦥', 'Text 🐈'], array_keys($grouped_definitions));
125+
126+
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions(NULL, 'label', 'id');
127+
$this->assertEquals(['general', 'number', 'text'], array_keys($grouped_definitions));
128+
}
129+
130+
/**
131+
* @covers ::getGroupedDefinitions
132+
*/
133+
public function testGetGroupedDefinitionsInvalid() {
134+
$this->discovery->getDefinitions()->willReturn([
135+
'string' => [
136+
'category' => 'text',
137+
'label' => 'Text (plain)',
138+
'id' => 'string',
139+
],
140+
]);
141+
142+
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([
143+
'general' => [
144+
'label' => 'General',
145+
'id' => 'general',
146+
],
147+
]);
148+
149+
$zend_assertions_default = ini_get('zend.assertions');
150+
$assert_active_default = assert_options(ASSERT_ACTIVE);
151+
152+
// Test behavior when assertions are not enabled.
153+
ini_set('zend.assertions', 0);
154+
assert_options(ASSERT_ACTIVE, 0);
155+
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions();
156+
$this->assertEquals(['General'], array_keys($grouped_definitions));
157+
158+
// Test behavior when assertions are enabled.
159+
ini_set('zend.assertions', 1);
160+
assert_options(ASSERT_ACTIVE, 1);
161+
$this->expectException(\AssertionError::class);
162+
try {
163+
$this->fieldTypeManager->getGroupedDefinitions();
164+
}
165+
catch (\Exception $e) {
166+
// Reset the original assert values.
167+
ini_set('zend.assertions', $zend_assertions_default);
168+
assert_options(ASSERT_ACTIVE, $assert_active_default);
169+
170+
throw $e;
171+
}
172+
}
173+
174+
/**
175+
* @covers ::getGroupedDefinitions
176+
*/
177+
public function testGetGroupedDefinitionsEmpty() {
178+
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([]);
179+
$this->assertEquals([], $this->fieldTypeManager->getGroupedDefinitions([]));
180+
}
181+
182+
}

0 commit comments

Comments
 (0)