From aa68fabc1ae11c3827fe48e0a5b6e73388ec0290 Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 21:41:38 -0700 Subject: [PATCH 01/12] #3230397 Normalize the way core does. --- config_normalizer.services.yml | 4 + .../ConfigNormalizer/ConfigNormalizerSort.php | 81 +++--- ...tity_view_display.node.article.default.yml | 0 .../config/install/views.view.recipes.yml | 275 ++++++++++++++++++ tests/src/Kernel/ConfigNormalizerSortTest.php | 137 +++++++++ 5 files changed, 458 insertions(+), 39 deletions(-) create mode 100644 tests/fixtures/config/install/core.entity_view_display.node.article.default.yml create mode 100644 tests/fixtures/config/install/views.view.recipes.yml create mode 100644 tests/src/Kernel/ConfigNormalizerSortTest.php diff --git a/config_normalizer.services.yml b/config_normalizer.services.yml index dca7b34..3480b01 100644 --- a/config_normalizer.services.yml +++ b/config_normalizer.services.yml @@ -2,3 +2,7 @@ services: plugin.manager.config_normalizer: class: Drupal\config_normalizer\Plugin\ConfigNormalizerManager parent: default_plugin_manager + config_normalizer.config_sorter: + class: Drupal\config_normalizer\Config\ConfigSorter + arguments: + - '@config.typed' diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php index e627454..e6b5cf7 100644 --- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php +++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php @@ -3,12 +3,12 @@ namespace Drupal\config_normalizer\Plugin\ConfigNormalizer; use Drupal\config_normalizer\Plugin\ConfigNormalizerBase; +use Drupal\Core\Config\StorableConfigBase; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Symfony\Component\Yaml\Inline; +use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Recursively sorts a configuration array. - * * @ConfigNormalizer( * id = "sort", * label = @Translation("sort"), @@ -18,47 +18,50 @@ use Symfony\Component\Yaml\Inline; */ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFactoryPluginInterface { - /** - * {@inheritdoc} - */ - public function normalize($name, array &$data, array $context) { - // Only sort if the normalization mode is default. - if ($this->isDefaultModeContext($context)) { - // Recursively normalize and return. - $data = $this->normalizeArray($data); - } + protected TypedConfigManagerInterface $typedConfigManager; + + public function __construct(array $configuration, $plugin_id, $plugin_definition, TypedConfigManagerInterface $typed_config_manager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->typedConfigManager = $typed_config_manager; + } + + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('config.typed') + ); } - /** - * Recursively sorts an array by key. - * - * @param array $array - * An array to normalize. - * - * @return array - * An array that is sorted by key, at each level of the array, with empty - * arrays removed. - */ - protected function normalizeArray(array $array) { - foreach ($array as $key => $value) { - if (is_array($value)) { - $new = $this->normalizeArray($value); - if (count($new)) { - $array[$key] = $new; + public function normalize($name, array &$data, array $context = []) { + $sorter = new class($this->typedConfigManager) extends StorableConfigBase { + public function anonymousSort(string $name, array $data): array { + self::validateName($name); + $this->validateKeys($data); + $this->setName($name)->initWithData($data); + + if ($this->typedConfigManager->hasConfigSchema($this->name)) { + $this->data = $this->castValue(NULL, $this->data); + } + else { + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } } + return $this->data; } - } - // If the array is associative, sort by key. - if (Inline::isHash($array)) { - ksort($array); - } - // Otherwise, sort by value. - else { - sort($array); - } + public function save($has_trusted_data = FALSE) { + throw new \LogicException('Save not supported.'); + } - return $array; - } + public function delete() { + throw new \LogicException('Delete not supported.'); + } + }; + $data = $sorter->anonymousSort($name, $data); + } } + diff --git a/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml b/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/config/install/views.view.recipes.yml b/tests/fixtures/config/install/views.view.recipes.yml new file mode 100644 index 0000000..3a7aae6 --- /dev/null +++ b/tests/fixtures/config/install/views.view.recipes.yml @@ -0,0 +1,275 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.card + - node.type.recipe + - system.menu.main + module: + - node + - user +id: recipes +label: Recipes +module: views +description: 'Recipes listing' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: Recipes + fields: + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: title + plugin_id: field + label: '' + exclude: false + alter: + alter_text: false + make_link: false + absolute: false + word_boundary: false + ellipsis: false + strip_tags: false + trim: false + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: true + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + pager: + type: mini + options: + offset: 0 + pagination_heading_level: h4 + items_per_page: 12 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + empty: { } + sorts: + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: created + exposed: false + granularity: second + nid: + id: nid + table: node_field_data + field: nid + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: nid + plugin_id: standard + order: ASC + expose: + label: '' + field_identifier: nid + exposed: false + arguments: { } + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + operator_limit_selection: false + operator_list: { } + type: + id: type + table: node_field_data + field: type + entity_type: node + entity_field: type + plugin_id: bundle + value: + recipe: recipe + group: 1 + expose: + operator_limit_selection: false + operator_list: { } + default_langcode: + id: default_langcode + table: node_field_data + field: default_langcode + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: default_langcode + plugin_id: boolean + operator: '=' + value: '1' + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + filter_groups: + operator: AND + groups: + 1: AND + style: + type: grid_responsive + options: + uses_fields: false + columns: 4 + cell_min_width: 240 + grid_gutter: 14 + alignment: horizontal + row: + type: 'entity:node' + options: + relationship: none + view_mode: card + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + css_class: '' + header: { } + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + rendering_language: '***LANGUAGE_language_content***' + display_extenders: { } + path: recipes + menu: + type: normal + title: Recipes + description: '' + weight: 30 + expanded: false + menu_name: main + parent: '' + context: '0' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php new file mode 100644 index 0000000..fe97380 --- /dev/null +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -0,0 +1,137 @@ +<?php + +namespace Drupal\Tests\config_normalizer\Kernel; + +use Symfony\Component\Yaml\Yaml; +use Drupal\KernelTests\KernelTestBase; + +/** + * Tests sorting configuration. + * + * @group config_normalizer + */ +class ConfigNormalizerSortTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'system', + 'user', + 'node', + 'views', + 'field', + 'filter', + 'text', + 'media', + 'image', + 'file', + 'menu_link_content', + 'block', + 'block_content', + 'layout_builder', + 'config_normalizer', + ]; + + /** + * @var \Drupal\config_normalizer\Config\ConfigSorter + */ + protected $configSorter; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); + $this->installEntitySchema('media'); + $this->installEntitySchema('file'); + $this->installEntitySchema('block_content'); + + $this->installConfig([ + 'system', 'node', 'views', 'image', 'media', 'text', + 'user', 'filter', 'block_content', 'layout_builder', + ]); + + // Explicitly copy fixtures to sync storage: + foreach ([ + 'core.entity_view_display.node.article.default', + 'views.view.recipes', + ] as $config_name) { + $this->copyFixtureToSyncStorage($config_name); + $data = $this->container->get('config.storage.sync')->read($config_name); + $this->config($config_name)->setData($data)->save(TRUE); + } + // Inject sorter properly after container rebuild: + $this->configSorter = $this->container->get('config_normalizer.config_sorter'); + + // Ensure container is properly built (critical step): + $this->container->get('kernel')->rebuildContainer(); + + } + /** + * Tests that configuration is normalized in a consistent manner. + * + * This ensures that configuration arrays are always sorted the same way, + * regardless of the original order in which they were defined or exported. + * The test loads known configuration from the Umami profile, shuffles its + * keys, and then verifies that sorting restores it to the expected order. + */ + public function testUmamiConfigurationSorting(): void { + $config_names = [ + 'views.view.recipes', + 'core.entity_view_display.node.article.default', + ]; + + foreach ($config_names as $config_name) { + // Retrieve the original configuration data. + $original = $this->config($config_name)->getRawData(); + // Apply the sorting mechanism to the original data. + $sorted_original = $this->configSorter->sort($config_name, $original); + + // Shuffle the configuration data to simulate disorder. + $shuffled = $this->shuffleAssocArray($original); + // Apply the sorting mechanism to the shuffled data. + $sorted_shuffled = $this->configSorter->sort($config_name, $shuffled); + + // Assert that sorting restores the original, expected order. + $this->assertSame( + $sorted_original, + $sorted_shuffled, + sprintf('Normalized config matches for "%s".', $config_name) + ); + } + } + + /** + * Helper to shuffle associative arrays. + */ + protected function shuffleAssocArray(array $array): array { + $keys = array_keys($array); + shuffle($keys); + $shuffled = []; + foreach ($keys as $key) { + $shuffled[$key] = is_array($array[$key]) ? $this->shuffleAssocArray($array[$key]) : $array[$key]; + } + return $shuffled; + } + + /** + * Copy fixture to sync storage. + */ + protected function copyFixtureToSyncStorage(string $config_name): void { + $module_path = $this->container->get('extension.list.module')->getPath('config_normalizer'); + $source = DRUPAL_ROOT . "/$module_path/tests/fixtures/config/install/$config_name.yml"; + + if (!file_exists($source)) { + $this->fail("Fixture file not found: $source"); + } + + $data = Yaml::parseFile($source); + $sync_storage = $this->container->get('config.storage.sync'); + $sync_storage->write($config_name, $data); + } + +} -- GitLab From de8296e5eaedb6da50b736342b8adc5857235f1b Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 21:43:40 -0700 Subject: [PATCH 02/12] add new code. --- src/Config/ConfigSorter.php | 0 .../ConfigNormalizer/ConfigNormalizerSort.php | 74 +++++++++++-------- 2 files changed, 42 insertions(+), 32 deletions(-) create mode 100644 src/Config/ConfigSorter.php diff --git a/src/Config/ConfigSorter.php b/src/Config/ConfigSorter.php new file mode 100644 index 0000000..e69de29 diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php index e6b5cf7..df5778d 100644 --- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php +++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php @@ -3,7 +3,7 @@ namespace Drupal\config_normalizer\Plugin\ConfigNormalizer; use Drupal\config_normalizer\Plugin\ConfigNormalizerBase; -use Drupal\Core\Config\StorableConfigBase; +use Drupal\config_normalizer\Config\ConfigSorter; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -18,50 +18,60 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFactoryPluginInterface { + /** + * @var \Drupal\Core\Config\TypedConfigManagerInterface + */ protected TypedConfigManagerInterface $typedConfigManager; - public function __construct(array $configuration, $plugin_id, $plugin_definition, TypedConfigManagerInterface $typed_config_manager) { + /** + * @var \Drupal\config_normalizer\Config\ConfigSorter + */ + protected ConfigSorter $configSorter; + + /** + * ConfigNormalizerSort constructor. + * + * @param array $configuration + * The plugin configuration. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager + * The typed config manager service. + * @param \Drupal\config_normalizer\Config\ConfigSorter $config_sorter + * * The config sorter service. + */ + public function __construct( + array $configuration, + $plugin_id, + $plugin_definition, + TypedConfigManagerInterface $typed_config_manager, + ConfigSorter $config_sorter, + ) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->typedConfigManager = $typed_config_manager; + $this->configSorter = $config_sorter; } + /** + * + */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, - $container->get('config.typed') + $container->get('config.typed'), + $container->get('config_normalizer.config_sorter') ); } - public function normalize($name, array &$data, array $context = []) { - $sorter = new class($this->typedConfigManager) extends StorableConfigBase { - public function anonymousSort(string $name, array $data): array { - self::validateName($name); - $this->validateKeys($data); - $this->setName($name)->initWithData($data); - - if ($this->typedConfigManager->hasConfigSchema($this->name)) { - $this->data = $this->castValue(NULL, $this->data); - } - else { - foreach ($this->data as $key => $value) { - $this->validateValue($key, $value); - } - } - return $this->data; - } - - public function save($has_trusted_data = FALSE) { - throw new \LogicException('Save not supported.'); - } - - public function delete() { - throw new \LogicException('Delete not supported.'); - } - }; - - $data = $sorter->anonymousSort($name, $data); + /** + * + */ + public function normalize($name, array &$data, array $context = []): array { + $data = $this->configSorter->sort($name, $data); } -} +} -- GitLab From 1faafa608d30e5f166b1fccfb64c14fe61db95be Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 21:44:25 -0700 Subject: [PATCH 03/12] add new code. --- src/Config/ConfigSorter.php | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/Config/ConfigSorter.php b/src/Config/ConfigSorter.php index e69de29..cc1a740 100644 --- a/src/Config/ConfigSorter.php +++ b/src/Config/ConfigSorter.php @@ -0,0 +1,78 @@ +<?php + +namespace Drupal\config_normalizer\Config; + +use Drupal\Core\Config\StorableConfigBase; +use Drupal\Core\Config\TypedConfigManagerInterface; + +/** + * Provides schema-based sorting of configuration arrays. + * + * @internal Uses core's internal sorting logic introduced by #2852557. + */ +class ConfigSorter { + + protected TypedConfigManagerInterface $typedConfigManager; + + public function __construct(TypedConfigManagerInterface $typedConfigManager) { + $this->typedConfigManager = $typedConfigManager; + } + + /** + * + */ + public function sort(string $name, array $data): array { + $sorter = new class($this->typedConfigManager) extends StorableConfigBase { + + public function __construct(TypedConfigManagerInterface $typedConfigManager) { + $this->typedConfigManager = $typedConfigManager; + } + + /** + * + */ + protected function getSchemaWrapper() { + if (!isset($this->schemaWrapper)) { + $this->schemaWrapper = $this->typedConfigManager->createFromNameAndData($this->name, $this->data); + } + return $this->schemaWrapper; + } + + /** + * + */ + public function anonymousSort(string $name, array $data): array { + $this->setName($name)->initWithData($data); + + if ($this->typedConfigManager->hasConfigSchema($name)) { + $this->data = $this->castValue(NULL, $this->data); + } + else { + foreach ($this->data as $key => $value) { + $this->validateValue($key, $value); + } + } + + return $this->data; + } + + /** + * + */ + public function save($has_trusted_data = FALSE) { + throw new \LogicException('Saving is not supported.'); + } + + /** + * + */ + public function delete() { + throw new \LogicException('Deletion is not supported.'); + } + + }; + + return $sorter->anonymousSort($name, $data); + } + +} -- GitLab From 813c4ccbd93654ce98ce2fe39a430f1c6061be5b Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 21:50:18 -0700 Subject: [PATCH 04/12] Update code comments' --- src/Config/ConfigSorter.php | 73 ++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/src/Config/ConfigSorter.php b/src/Config/ConfigSorter.php index cc1a740..e79c500 100644 --- a/src/Config/ConfigSorter.php +++ b/src/Config/ConfigSorter.php @@ -8,63 +8,133 @@ use Drupal\Core\Config\TypedConfigManagerInterface; /** * Provides schema-based sorting of configuration arrays. * - * @internal Uses core's internal sorting logic introduced by #2852557. + * This class leverages Drupal's internal sorting logic introduced in + * https://www.drupal.org/project/drupal/issues/2852557 to sort configuration + * data based on its schema. */ class ConfigSorter { + /** + * The typed configuration manager service. + * + * @var \Drupal\Core\Config\TypedConfigManagerInterface + */ protected TypedConfigManagerInterface $typedConfigManager; + /** + * Constructs a ConfigSorter object. + * + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager + * The typed configuration manager service to manage configuration schemas. + */ public function __construct(TypedConfigManagerInterface $typedConfigManager) { $this->typedConfigManager = $typedConfigManager; } /** + * Sorts a configuration array based on its schema. + * + * This method creates an anonymous class that extends StorableConfigBase + * and uses the internal sorting mechanism to sort the provided configuration + * data according to the schema for the given configuration name. * + * @param string $name + * The name of the configuration to be sorted. + * @param array $data + * The configuration data to be sorted. + * + * @return array + * The sorted configuration data. */ public function sort(string $name, array $data): array { + // Create an anonymous class that extends StorableConfigBase + // for sorting the configuration data based on its schema. $sorter = new class($this->typedConfigManager) extends StorableConfigBase { + /** + * Constructs the anonymous class instance. + * + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager + * The typed configuration manager service. + */ public function __construct(TypedConfigManagerInterface $typedConfigManager) { $this->typedConfigManager = $typedConfigManager; } /** + * Retrieves the schema wrapper for the configuration data. * + * This method creates and returns the schema wrapper for the given + * configuration data using the TypedConfigManager service. + * + * @return \Drupal\Core\Config\ConfigSchemaInterface + * The schema wrapper for the configuration data. */ protected function getSchemaWrapper() { if (!isset($this->schemaWrapper)) { + // Create the schema wrapper if it hasn't been created already. $this->schemaWrapper = $this->typedConfigManager->createFromNameAndData($this->name, $this->data); } return $this->schemaWrapper; } /** + * Sorts the configuration data based on the schema. + * + * This method checks if the configuration has a schema, casts the value if + * necessary, and validates the data values before returning the sorted data. * + * @param string $name + * The name of the configuration to be sorted. + * @param array $data + * The configuration data to be sorted. + * + * @return array + * The sorted configuration data. */ public function anonymousSort(string $name, array $data): array { + // Set the name and initialize the data for sorting. $this->setName($name)->initWithData($data); + // If a schema exists, cast the value according to the schema. if ($this->typedConfigManager->hasConfigSchema($name)) { $this->data = $this->castValue(NULL, $this->data); } else { + // If no schema, validate the data values. foreach ($this->data as $key => $value) { $this->validateValue($key, $value); } } + // Return the sorted configuration data. return $this->data; } /** + * Throws a LogicException when attempting to save the configuration. + * + * This method is not supported in the current class and will always throw + * an exception. * + * @param bool $has_trusted_data + * Whether the data has been trusted. Not used in this case. + * + * @throws \LogicException + * Always throws an exception. */ public function save($has_trusted_data = FALSE) { throw new \LogicException('Saving is not supported.'); } /** + * Throws a LogicException when attempting to delete the configuration. + * + * This method is not supported in the current class and will always throw + * an exception. * + * @throws \LogicException + * Always throws an exception. */ public function delete() { throw new \LogicException('Deletion is not supported.'); @@ -72,6 +142,7 @@ class ConfigSorter { }; + // Return the sorted configuration data. return $sorter->anonymousSort($name, $data); } -- GitLab From da6aad8ffea49aa7976842700533cd7092cdc129 Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 21:52:49 -0700 Subject: [PATCH 05/12] Update code comments' --- ...tity_view_display.node.article.default.yml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml b/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml index e69de29..f2665c8 100644 --- a/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml +++ b/tests/fixtures/config/install/core.entity_view_display.node.article.default.yml @@ -0,0 +1,55 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.article.field_body + - field.field.node.article.field_media_image + - field.field.node.article.field_tags + - field.field.node.article.layout_builder__layout + - node.type.article + module: + - layout_builder + - text + - user +third_party_settings: + layout_builder: + enabled: false + allow_custom: false +id: node.article.default +targetEntityType: node +bundle: article +mode: default +content: + field_body: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 3 + region: content + field_media_image: + type: entity_reference_entity_view + label: hidden + settings: + view_mode: responsive_3x2 + link: false + third_party_settings: { } + weight: 2 + region: content + field_tags: + type: entity_reference_label + label: above + settings: + link: true + third_party_settings: { } + weight: 0 + region: content + links: + settings: { } + third_party_settings: { } + weight: 4 + region: content +hidden: + content_moderation_control: true + langcode: true + layout_builder__layout: true -- GitLab From e2f0ba3b0f15b29fd8787fde7b995e994714136a Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 22:00:06 -0700 Subject: [PATCH 06/12] Code quality updates --- src/Config/ConfigSorter.php | 5 ++-- .../ConfigNormalizer/ConfigNormalizerSort.php | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/Config/ConfigSorter.php b/src/Config/ConfigSorter.php index e79c500..b767219 100644 --- a/src/Config/ConfigSorter.php +++ b/src/Config/ConfigSorter.php @@ -81,8 +81,9 @@ class ConfigSorter { /** * Sorts the configuration data based on the schema. * - * This method checks if the configuration has a schema, casts the value if - * necessary, and validates the data values before returning the sorted data. + * This method checks if the configuration has a schema, casts the value + * if necessary, and validates the data values before returning + * the sorted data. * * @param string $name * The name of the configuration to be sorted. diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php index df5778d..12af75e 100644 --- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php +++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php @@ -9,6 +9,8 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** + * ConfigNormalizer plugin to recursively sort a configuration array. + * * @ConfigNormalizer( * id = "sort", * label = @Translation("sort"), @@ -19,11 +21,15 @@ use Symfony\Component\DependencyInjection\ContainerInterface; class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFactoryPluginInterface { /** + * The typed configuration manager service. + * * @var \Drupal\Core\Config\TypedConfigManagerInterface */ protected TypedConfigManagerInterface $typedConfigManager; /** + * The configuration sorter service. + * * @var \Drupal\config_normalizer\Config\ConfigSorter */ protected ConfigSorter $configSorter; @@ -40,14 +46,14 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager * The typed config manager service. * @param \Drupal\config_normalizer\Config\ConfigSorter $config_sorter - * * The config sorter service. + * The config sorter service. */ public function __construct( array $configuration, $plugin_id, $plugin_definition, TypedConfigManagerInterface $typed_config_manager, - ConfigSorter $config_sorter, + ConfigSorter $config_sorter ) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->typedConfigManager = $typed_config_manager; @@ -67,11 +73,25 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact ); } + /** + * Normalizes the configuration data by sorting it recursively. + * + * This method is called to sort the configuration data based on its schema. + * It uses the ConfigSorter service to perform the sorting operation. + * + * @param string $name + * The name of the configuration. + * @param array &$data + * The configuration data to be normalized (sorted). + * @param array $context + * The context in which the normalization takes place. * + * @return array + * The sorted configuration data. */ public function normalize($name, array &$data, array $context = []): array { - $data = $this->configSorter->sort($name, $data); + return $this->configSorter->sort($name, $data); } } -- GitLab From 4ee379d0d8ed6fc19a9e7249bc4650335a088a1b Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 22:06:07 -0700 Subject: [PATCH 07/12] Code quality updates --- src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php | 5 ++--- tests/src/Kernel/ConfigNormalizerSortTest.php | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php index 12af75e..20fff3e 100644 --- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php +++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php @@ -53,7 +53,7 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact $plugin_id, $plugin_definition, TypedConfigManagerInterface $typed_config_manager, - ConfigSorter $config_sorter + ConfigSorter $config_sorter, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->typedConfigManager = $typed_config_manager; @@ -61,7 +61,7 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact } /** - * + * Creates an instance of the ConfigNormalizerSort plugin. */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( @@ -73,7 +73,6 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact ); } - /** * Normalizes the configuration data by sorting it recursively. * diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php index fe97380..a5207b3 100644 --- a/tests/src/Kernel/ConfigNormalizerSortTest.php +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -34,6 +34,8 @@ class ConfigNormalizerSortTest extends KernelTestBase { ]; /** + * The configuration sorter service. + * * @var \Drupal\config_normalizer\Config\ConfigSorter */ protected $configSorter; @@ -71,6 +73,7 @@ class ConfigNormalizerSortTest extends KernelTestBase { $this->container->get('kernel')->rebuildContainer(); } + /** * Tests that configuration is normalized in a consistent manner. * -- GitLab From eab73a69422f183735167430d598a2b8738831e8 Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 22:07:52 -0700 Subject: [PATCH 08/12] Code quality updates --- src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php index 20fff3e..c9960a2 100644 --- a/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php +++ b/src/Plugin/ConfigNormalizer/ConfigNormalizerSort.php @@ -55,7 +55,12 @@ class ConfigNormalizerSort extends ConfigNormalizerBase implements ContainerFact TypedConfigManagerInterface $typed_config_manager, ConfigSorter $config_sorter, ) { - parent::__construct($configuration, $plugin_id, $plugin_definition); + parent::__construct( + $configuration, + $plugin_id, + $plugin_definition, + $typed_config_manager, + ); $this->typedConfigManager = $typed_config_manager; $this->configSorter = $config_sorter; } -- GitLab From dbed00f58b06a84fb93212a1ab9b402de04cc5af Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 22:50:19 -0700 Subject: [PATCH 09/12] tests --- tests/src/Kernel/ConfigNormalizerSortTest.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php index a5207b3..39f5ea8 100644 --- a/tests/src/Kernel/ConfigNormalizerSortTest.php +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -99,12 +99,27 @@ class ConfigNormalizerSortTest extends KernelTestBase { // Apply the sorting mechanism to the shuffled data. $sorted_shuffled = $this->configSorter->sort($config_name, $shuffled); + // Assert shuffled data is not equal to the original. + $this->assertNotEqual( + $sorted_original, + $sorted_shuffled, + sprintf('Normalized config should not match for "%s".', $config_name) + ); + // Assert that sorting restores the original, expected order. + $this->assertEqual( + $sorted_original, + $sorted_shuffled, + sprintf('Normalized config should match for "%s".', $config_name) + ); + + // assertSame is more strict than assertEqual, ensuring both type and value match. $this->assertSame( $sorted_original, $sorted_shuffled, - sprintf('Normalized config matches for "%s".', $config_name) + sprintf('Normalized config should match for "%s".', $config_name) ); + } } -- GitLab From 57b24b2362294ac249caa3a62e89074b6356a21b Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 22:52:21 -0700 Subject: [PATCH 10/12] tests --- tests/src/Kernel/ConfigNormalizerSortTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php index 39f5ea8..6c59b0b 100644 --- a/tests/src/Kernel/ConfigNormalizerSortTest.php +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -100,14 +100,14 @@ class ConfigNormalizerSortTest extends KernelTestBase { $sorted_shuffled = $this->configSorter->sort($config_name, $shuffled); // Assert shuffled data is not equal to the original. - $this->assertNotEqual( + $this->assertNotEquals( $sorted_original, $sorted_shuffled, sprintf('Normalized config should not match for "%s".', $config_name) ); // Assert that sorting restores the original, expected order. - $this->assertEqual( + $this->assertEquals( $sorted_original, $sorted_shuffled, sprintf('Normalized config should match for "%s".', $config_name) -- GitLab From 1d80e3be78de8185989698f2c6fc697a64f5eeac Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Tue, 18 Mar 2025 23:03:32 -0700 Subject: [PATCH 11/12] tests --- tests/src/Kernel/ConfigNormalizerSortTest.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php index 6c59b0b..72e91d8 100644 --- a/tests/src/Kernel/ConfigNormalizerSortTest.php +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -100,25 +100,25 @@ class ConfigNormalizerSortTest extends KernelTestBase { $sorted_shuffled = $this->configSorter->sort($config_name, $shuffled); // Assert shuffled data is not equal to the original. - $this->assertNotEquals( - $sorted_original, - $sorted_shuffled, - sprintf('Normalized config should not match for "%s".', $config_name) - ); - - // Assert that sorting restores the original, expected order. $this->assertEquals( $sorted_original, $sorted_shuffled, - sprintf('Normalized config should match for "%s".', $config_name) + sprintf('The sorted shuffled version and sorted original of "%s" are not equal.', $config_name) ); - // assertSame is more strict than assertEqual, ensuring both type and value match. - $this->assertSame( - $sorted_original, - $sorted_shuffled, - sprintf('Normalized config should match for "%s".', $config_name) - ); + // // Assert that sorting restores the original, expected order. + // $this->assertEquals( + // $sorted_original, + // $sorted_shuffled, + // sprintf('Normalized config should match for "%s".', $config_name) + // ); + + // // assertSame is more strict than assertEqual, ensuring both type and value match. + // $this->assertSame( + // $sorted_original, + // $sorted_shuffled, + // sprintf('Normalized config should match for "%s".', $config_name) + // ); } } -- GitLab From 4064b408b697a25b3cbf86b9d3cb8daca5fd7324 Mon Sep 17 00:00:00 2001 From: Chris Green <chrisgreen@arizona.edu> Date: Mon, 17 Mar 2025 21:29:27 -0700 Subject: [PATCH 12/12] tests --- tests/src/Kernel/ConfigNormalizerSortTest.php | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/src/Kernel/ConfigNormalizerSortTest.php b/tests/src/Kernel/ConfigNormalizerSortTest.php index 72e91d8..2c92134 100644 --- a/tests/src/Kernel/ConfigNormalizerSortTest.php +++ b/tests/src/Kernel/ConfigNormalizerSortTest.php @@ -106,19 +106,13 @@ class ConfigNormalizerSortTest extends KernelTestBase { sprintf('The sorted shuffled version and sorted original of "%s" are not equal.', $config_name) ); - // // Assert that sorting restores the original, expected order. - // $this->assertEquals( - // $sorted_original, - // $sorted_shuffled, - // sprintf('Normalized config should match for "%s".', $config_name) - // ); - - // // assertSame is more strict than assertEqual, ensuring both type and value match. - // $this->assertSame( - // $sorted_original, - // $sorted_shuffled, - // sprintf('Normalized config should match for "%s".', $config_name) - // ); + // The assertSame method is more strict than assertEqual, + // ensuring both type and value match. + $this->assertSame( + $sorted_original, + $sorted_shuffled, + sprintf('The sorted shuffled version and sorted original of "%s" are not the same.', $config_name) + ); } } -- GitLab