Problem/Motivation

Trying to use the #config_target on text_format form elements in a configuration form results in a validation exception:

LogicException: Two #config_targets both target "akey.asubkey" in the "mymodule.settings" config: `$form['parent']['akey']['asubkey']` and `$form['parent']['akey']['asubkey']['value']`. in Drupal\Core\Form\ConfigFormBase->doStoreConfigMap() (line 183 of core/lib/Drupal/Core/Form/ConfigFormBase.php).

Steps to reproduce

  • Create a new configuration form extending \Drupal\Core\Form\ConfigFormBase
  • Add a nested text_format field with #config_target such as:

            $form['parent']['akey']['asubkey'] = [
              '#type' => 'text_format',
              '#title' => $this->t('Subkey title'),
              '#config_target' => "mymodule.settings:akey.asubkey",
            ];
      
  • Submit the form

Proposed resolution

Do not copy #config_target to prevent multiple elements targeting the same property.

Remaining tasks

  1. Review and validate the changes to ensure they resolve the issue without introducing new problems.
  2. Evaluate the added PHPUnit test case to ensure it is thorough and effective, and determine if any further improvements are needed.

Issue fork drupal-3463868

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

matthieuscarset created an issue. See original summary.

cilefen’s picture

Version: 10.2.x-dev » 11.x-dev

This is the working branch.

VinmayiSwamy made their first commit to this issue’s fork.

vinmayiswamy’s picture

Hi, to resolve this issue, the following modifications were made to the ConfigFormBase class:

Enhanced Handling of text_format Elements:
- Added a special case handling for text_format elements within the doStoreConfigMap() method.
- This ensures that text_format elements are properly mapped to their value and format properties by updating the $map array with these keys.

Code Modifications:

  1. doStoreConfigMap() Method:
  2. - Checked if the element type is text_format.
    - If true, the #config_target for text_format fields is now mapped separately for both .value and .format properties. This prevents the conflict of having two #config_targets pointing to the same configuration key.
    - Added logic to handle #array_parents as arrays and applied proper mapping to avoid duplication issues.

  3. copyFormValuesToConfig() Method:
  4. - Updated to ensure that $array_parents is always treated as an array when retrieving form elements.
    - Added logic to skip the iteration if the parent path is not found and to ensure that elements have the #config_target property set before processing.

The modifications ensure that text_format elements are handled correctly by addressing the issue of duplicate configuration targets. By implementing specific logic for text_format fields and updating the configuration mapping process, the fix prevents validation exceptions and maintains consistency across the configuration form.

Please review the changes to confirm that they address the issue effectively. Your feedback and suggestions for further improvements are greatly appreciated.

Thanks!

vinmayiswamy’s picture

Status: Active » Needs review
smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests
vinmayiswamy’s picture

Status: Needs work » Needs review

Hi, I have added a PHPUnit test case to verify the changes made to the doStoreConfigMap method in the ConfigFormBase class. This test is designed to check how the method handles text_format elements with #config_target properties and ensures that the configuration mappings are updated correctly.

In this test, I created a mock of FormStateInterface to simulate the form state and validate that the doStoreConfigMap method interacts with it as expected. Since doStoreConfigMap is a protected method, I used reflection to access and invoke it.

The test involves setting up a mock for FormStateInterface and specifying an expectation for the set method to be called with the key 'config_targets' and a particular map. I then used reflection to invoke the protected doStoreConfigMap method on a mocked instance of ConfigFormBase. The sample form element defined includes text_format and #config_target attributes, as well as #array_parents for validation.

The focus of the test is to ensure that the config_targets map contains the correct entries and values for text_format elements, verifying that the #config_target properties are processed appropriately.

Could anyone please advise if a functional test is also required in addition to this unit test? While this unit test verifies the method in isolation, a functional test might be needed to assess its behaviour within the broader application context.

As this is my first attempt at writing test cases, I would really appreciate any feedback on whether this test is thorough and meets all necessary criteria. If there are additional scenarios to consider or best practices to follow, your guidance would be very helpful.

Please review the test case and let me know if any further steps are needed or if improvements should be made.

Thanks!

smustgrave’s picture

Status: Needs review » Needs work

Just noticed issue summary is incomplete.

vinmayiswamy’s picture

Issue summary: View changes
vinmayiswamy’s picture

Hi, I have updated the issue summary. Please review the changes and let me know if any further changes are required. Thank you!

vinmayiswamy’s picture

Status: Needs work » Needs review
smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs tests +Needs Review Queue Initiative

https://git.drupalcode.org/issue/drupal-3463868/-/jobs/2381869 shows the test coverage so removing that tag.

Believe feedback has been addressed.

catch’s picture

Component: filter.module » configuration system
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs subsystem maintainer review

I think we should look at whether this can be done without special-casing text_format in the config system - the exception is designed to prevent broken usage, and if the usage isn't broken then we should be able to account for that. Other config could have a similar structure.

idebr made their first commit to this issue’s fork.

idebr’s picture

MR 10182 implements an alternative solution where the code ends up as follows:

$form['parent']['akey']['asubkey'] = [
  '#type' => 'text_format',
  '#title' => $this->t('Subkey title'),
  '#format' => $this->config('mymodule.settings')->get('akey.asubkey.format'),
  '#config_target' => new ConfigTarget('mymodule.settings', 'akey.asubkey', fromConfig: fn (?array $value = NULL): string => $value['value'] ?? ''),
];

I'm not sure ConfigTarget was intended to control any element property other than #default_value when reading the change record at https://www.drupal.org/node/3373502

nickdickinsonwilde’s picture

Status: Needs work » Reviewed & tested by the community

MR 10182, resolves the issue, and seems like the least noisy solution and most future safe.

catch’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs tests

That looks great but could we add some test coverage?

smustgrave’s picture

Status: Needs review » Needs work

Yea the new MR dropped the test coverage from the original MR. One should also be close.d

idebr’s picture

StatusFileSize
new3.73 KB

I pushed a test to https://git.drupalcode.org/project/drupal/-/merge_requests/10182/commits but it is not showing up in Gitlab? Attached 3463868-21-test-only.patch contains the code

idebr’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests

Oh, the change showed up on gitlab

smustgrave changed the visibility of the branch 3463868-two-configtargets-error to hidden.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Cleaned up the old MRs so we should only have 1 with the test coverage https://git.drupalcode.org/issue/drupal-3463868/-/jobs/7129797

Restoring previous RTBC

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

idebr’s picture

idebr’s picture

Issue summary: View changes

Updated the Proposed resolution to match the current implementation

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

This still needs blessing from the subsystem maintainer, but I think this is looking real good. Currently TextFormat is the only element that has a `$keys_not_to_copy`, but if other would be doing the same, I think this solution would work for them as well.

alexpott made their first commit to this issue’s fork.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

This doesn't actually set the default value of the text field correctly...

idebr’s picture

@alexpott can you elaborate what "this" refers to in #32 and what is incorrect about it?

borisson_’s picture

I haven't manually tested this recently, but if this doesn't work we should also fix the test that is trying to test the behavior as well.

alexpott’s picture

The default value for the field is not set... so although the value is saved it is not displayed back to the user. This is because of how \Drupal\filter\Element\TextFormat::processFormat() works. I can't quite work out how to fix this yet.

alexpott’s picture

I just pushed a test that shows the problem.

idebr’s picture

Status: Needs work » Needs review

The URL under test returns a cache hit, as the form has no cache dependency on the config being edited.

I have added a cache tag specifically to the form being tested, but I can see the cache tag being added in ConfigFormBase instead. Perhaps in a follow-up?

alexpott’s picture

Status: Needs review » Needs work

@idebr I think that the cache tag should be added in ConfigFormBase instead. Great find. Definitely something we should do.

alexpott’s picture

We still to work out how the format can be saved and the default value should work.

alexpott’s picture

Status: Needs work » Needs review

Addressed #38 and #39

idebr’s picture

Status: Needs review » Needs work

Some new test failures on related functional and unit tests, see https://git.drupalcode.org/project/drupal/-/pipelines/778184

alexpott’s picture

Status: Needs work » Needs review

Fixed the tests.

idebr’s picture

Status: Needs review » Reviewed & tested by the community

Thanks! The additional changes look good to me

carlitus’s picture

The current diff from MR doesn't work for Drupal 11.3.8. Attaching a patch that works (without the tests)

  • catch committed e5162bb5 on 11.x
    fix: #3463868 Two #config_targets error when used on a text_format form...

  • catch committed 55e61b4a on main
    fix: #3463868 Two #config_targets error when used on a text_format form...
catch’s picture

Version: main » 11.3.x-dev
Status: Reviewed & tested by the community » Fixed

This looks great now. Issue went through some twists and turns but I first read the MR, then went back through the issue history to figure out how we ended up where we did and can't think of anything else.

Committed/pushed to main and cherry-picked to 11.x and 11.3.x thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • catch committed 712188eb on 11.3.x
    fix: #3463868 Two #config_targets error when used on a text_format form...

godotislate made their first commit to this issue’s fork.

godotislate’s picture

Status: Fixed » Needs review

This is causing test failures on 11.3.x: https://git.drupalcode.org/project/drupal/-/jobs/9744692
Since #3566768: Change \Drupal\system\Form\CronForm to use ConfigFormBase and use #config_target wasn't backported to 11.3.x, the CronForm doesn't extend ConfigFormBase, so the config:system.cron cache tag doesn't get added.

MR against 11.3.x to remove the tags from the tests: https://git.drupalcode.org/project/drupal/-/merge_requests/15716

alexpott’s picture

Status: Needs review » Fixed

Committed a50bc84 and pushed to 11.3.x. Thanks!

Thanks @godotislate - follow-up committed.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • alexpott committed a50bc847 on 11.3.x
    fix: follow-up #3463868 Two #config_targets error when used on a...