Skip to content
Snippets Groups Projects
Verified Commit e8568a29 authored by Dave Long's avatar Dave Long
Browse files

Issue #3371301 by lauriii, srishtiiee, longwave, smustgrave: Retrieve field...

Issue #3371301 by lauriii, srishtiiee, longwave, smustgrave: Retrieve field type category information in \Drupal\Core\Field\FieldTypePluginManager::getGroupedDefinitions
parent a5cd2d7a
Branches
Tags
44 merge requests!12227Issue #3181946 by jonmcl, mglaman,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!3878Removed unused condition head title for views,!38582585169-10.1.x,!3818Issue #2140179: $entity->original gets stale between updates,!3742Issue #3328429: Create item list field formatter for displaying ordered and unordered lists,!3731Claro: role=button on status report items,!3668Resolve #3347842 "Deprecate the trusted",!3651Issue #3347736: Create new SDC component for Olivero (header-search),!3546refactored dialog.pcss file,!3531Issue #3336994: StringFormatter always displays links to entity even if the user in context does not have access,!3502Issue #3335308: Confusing behavior with FormState::setFormState and FormState::setMethod,!3478Issue #3337882: Deleted menus are not removed from content type config,!3452Issue #3332701: Refactor Claro's tablesort-indicator stylesheet,!3451Issue #2410579: Allows setting the current language programmatically.,!3355Issue #3209129: Scrolling problems when adding a block via layout builder,!3226Issue #2987537: Custom menu link entity type should not declare "bundle" entity key,!3154Fixes #2987987 - CSRF token validation broken on routes with optional parameters.,!3147Issue #3328457: Replace most substr($a, $i) where $i is negative with str_ends_with(),!3146Issue #3328456: Replace substr($a, 0, $i) with str_starts_with(),!3133core/modules/system/css/components/hidden.module.css,!31312878513-10.1.x,!2964Issue #2865710 : Dependencies from only one instance of a widget are used in display modes,!2812Issue #3312049: [Followup] Fix Drupal.Commenting.FunctionComment.MissingReturnType returns for NULL,!2614Issue #2981326: Replace non-test usages of \Drupal::logger() with IoC injection,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!877Issue #2708101: Default value for link text is not saved,!844Resolve #3036010 "Updaters",!673Issue #3214208: FinishResponseSubscriber could create duplicate headers,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
......@@ -15,4 +15,5 @@ date_time:
description: 'Field to store date and time values.'
weight: -10
general:
label: 'General'
class: \Drupal\Core\Field\FallbackFieldTypeCategory
......@@ -79,7 +79,7 @@ protected function getDiscovery(): YamlDiscovery {
* {@inheritdoc}
*/
public function getFallbackPluginId($plugin_id, array $configuration = []): string {
return 'general';
return FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
}
}
......@@ -8,4 +8,10 @@
* Defines an interface for field type category managers.
*/
interface FieldTypeCategoryManagerInterface extends PluginManagerInterface {
/**
* Fallback category for field types.
*/
const FALLBACK_CATEGORY = 'general';
}
......@@ -18,11 +18,6 @@
*/
class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
/**
* Default category for field types.
*/
const DEFAULT_CATEGORY = 'general';
use CategorizingPluginManagerTrait {
getGroupedDefinitions as protected getGroupedDefinitionsTrait;
}
......@@ -107,11 +102,11 @@ public function processDefinition(&$definition, $plugin_id) {
if ($definition['category'] instanceof TranslatableMarkup) {
@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);
$definition['category'] = static::DEFAULT_CATEGORY;
$definition['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
}
elseif (empty($definition['category'])) {
// Ensure that every field type has a category.
$definition['category'] = static::DEFAULT_CATEGORY;
$definition['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
}
}
......@@ -164,20 +159,45 @@ public function getFieldSettingsSummary(FieldDefinitionInterface $field_definiti
}
/**
* {@inheritdoc}
* Gets sorted field type definitions grouped by category.
*
* In addition to grouping, both categories and its entries are sorted,
* whereas plugin definitions are sorted by label.
*
* @param array[]|null $definitions
* (optional) The plugin definitions to group. If omitted, all plugin
* definitions are used.
* @param string $label_key
* (optional) The array key to use as the label of the field type.
* @param string $category_label_key
* (optional) The array key to use as the label of the category.
*
* @return array[]
* Keys are category names, and values are arrays of which the keys are
* plugin IDs and the values are plugin definitions.
*/
public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label', $category_label_key = 'label') {
$grouped_categories = $this->getGroupedDefinitionsTrait($definitions, $label_key);
$category_info = $this->fieldTypeCategoryManager->getDefinitions();
// Ensure that all the referenced categories exist.
foreach ($grouped_categories as $group => $definitions) {
if (!isset($category_info[$group]) && $group !== static::DEFAULT_CATEGORY) {
if (!isset($category_info[$group])) {
assert(FALSE, "\"$group\" must be defined in MODULE_NAME.field_type_categories.yml");
$grouped_categories[static::DEFAULT_CATEGORY] += $definitions;
if (!isset($grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY])) {
$grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY] = [];
}
$grouped_categories[FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY] += $definitions;
unset($grouped_categories[$group]);
}
}
return $grouped_categories;
$normalized_grouped_categories = [];
foreach ($grouped_categories as $group => $definitions) {
$normalized_grouped_categories[(string) $category_info[$group][$category_label_key]] = $definitions;
}
return $normalized_grouped_categories;
}
/**
......
......@@ -156,7 +156,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
];
$field_type_options = $unique_definitions = [];
$grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions());
$grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions(), 'label', 'id');
// Invoke a hook to get category properties.
foreach ($grouped_definitions as $category => $field_types) {
foreach ($field_types as $name => $field_type) {
......@@ -165,6 +165,9 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
$category_plugin = $this->fieldTypeCategoryManager->createInstance($category, $unique_definitions[$category][$name]);
$field_type_options[$category_plugin->getPluginId()] = ['unique_identifier' => $name] + $field_type;
}
else {
$field_type_options[(string) $field_type['label']] = ['unique_identifier' => $name] + $field_type;
}
}
}
$form['add-label'] = [
......
......@@ -9,7 +9,7 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldTypePluginManager;
use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElement;
......@@ -187,7 +187,7 @@ function media_field_ui_preconfigured_options_alter(array &$options, $field_type
if (!empty($options['media'])) {
$options['media']['description'] = t('Field to reference media. Allows uploading and selecting from uploaded media.');
$options['media']['weight'] = -25;
$options['media']['category'] = FieldTypePluginManager::DEFAULT_CATEGORY;
$options['media']['category'] = FieldTypeCategoryManagerInterface::FALLBACK_CATEGORY;
$options['media']['entity_view_display']['type'] = 'entity_reference_entity_view';
}
}
......
<?php
namespace Drupal\Tests\Core\Field;
use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldTypeCategoryManagerInterface;
use Drupal\Core\Field\FieldTypePluginManager;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\TypedDataManager;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
/**
* @coversDefaultClass \Drupal\Core\Field\FieldTypePluginManager
* @group Field
*/
class FieldTypePluginManagerTest extends UnitTestCase {
/**
* The field type plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManager
*/
protected FieldTypePluginManager $fieldTypeManager;
/**
* A mocked module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $moduleHandler;
/**
* A mocked module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $fieldTypeCategoryManager;
/**
* A mocked plugin discovery.
*
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\Prophecy\Prophecy\ObjectProphecy
*/
protected $discovery;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$container = new ContainerBuilder();
$current_user = $this->prophesize(AccountInterface::class);
$container->set('current_user', $current_user->reveal());
$container->set('string_translation', $this->getStringTranslationStub());
\Drupal::setContainer($container);
$cache_backend = $this->prophesize(CacheBackendInterface::class);
$this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
$this->moduleHandler->alter('field_info', Argument::any())->willReturn(NULL);
$typed_data_manager = $this->prophesize(TypedDataManager::class);
$this->fieldTypeCategoryManager = $this->prophesize(FieldTypeCategoryManagerInterface::class);
$this->fieldTypeManager = new FieldTypePluginManager(new \ArrayObject(), $cache_backend->reveal(), $this->moduleHandler->reveal(), $typed_data_manager->reveal(), $this->fieldTypeCategoryManager->reveal());
$this->fieldTypeManager->setStringTranslation($this->getStringTranslationStub());
$this->discovery = $this->prophesize(DiscoveryInterface::class);
$property = new \ReflectionProperty(FieldTypePluginManager::class, 'discovery');
$property->setAccessible(TRUE);
$property->setValue($this->fieldTypeManager, $this->discovery->reveal());
}
/**
* @covers ::getGroupedDefinitions
*/
public function testGetGroupedDefinitions() {
$this->discovery->getDefinitions()->willReturn([
'telephone' => [
'category' => 'general',
'label' => 'Telephone',
'id' => 'telephone',
],
'string' => [
'category' => 'text',
'label' => 'Text (plain)',
'id' => 'string',
],
'integer' => [
'category' => 'number',
'label' => 'Number (integer)',
'id' => 'integer',
],
'float' => [
'id' => 'float',
'label' => 'Number (float)',
'category' => 'number',
],
]);
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([
'general' => [
'label' => 'General',
'id' => 'general',
],
'number' => [
'label' => 'Number 🦥',
'id' => 'number',
],
'text' => [
'label' => 'Text 🐈',
'id' => 'text',
],
'empty_group' => [
'label' => 'Empty 🦗',
'id' => 'empty_group',
],
]);
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions();
$this->assertEquals(['General', 'Number 🦥', 'Text 🐈'], array_keys($grouped_definitions));
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions(NULL, 'label', 'id');
$this->assertEquals(['general', 'number', 'text'], array_keys($grouped_definitions));
}
/**
* @covers ::getGroupedDefinitions
*/
public function testGetGroupedDefinitionsInvalid() {
$this->discovery->getDefinitions()->willReturn([
'string' => [
'category' => 'text',
'label' => 'Text (plain)',
'id' => 'string',
],
]);
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([
'general' => [
'label' => 'General',
'id' => 'general',
],
]);
$zend_assertions_default = ini_get('zend.assertions');
$assert_active_default = assert_options(ASSERT_ACTIVE);
// Test behavior when assertions are not enabled.
ini_set('zend.assertions', 0);
assert_options(ASSERT_ACTIVE, 0);
$grouped_definitions = $this->fieldTypeManager->getGroupedDefinitions();
$this->assertEquals(['General'], array_keys($grouped_definitions));
// Test behavior when assertions are enabled.
ini_set('zend.assertions', 1);
assert_options(ASSERT_ACTIVE, 1);
$this->expectException(\AssertionError::class);
try {
$this->fieldTypeManager->getGroupedDefinitions();
}
catch (\Exception $e) {
// Reset the original assert values.
ini_set('zend.assertions', $zend_assertions_default);
assert_options(ASSERT_ACTIVE, $assert_active_default);
throw $e;
}
}
/**
* @covers ::getGroupedDefinitions
*/
public function testGetGroupedDefinitionsEmpty() {
$this->fieldTypeCategoryManager->getDefinitions()->willReturn([]);
$this->assertEquals([], $this->fieldTypeManager->getGroupedDefinitions([]));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment