Problem/Motivation

Exposing a filter through the ai_agents_views:update_view_handler_option tool without explicitly supplying an expose.identifier saved the filter with an empty identifier - config that Drupal core's own View::validate() rejects outright ("The identifier is required if the filter is exposed."), and it has no usable query parameter even if validation were skipped, so a visitor can never actually submit the exposed filter.

An LLM asked to "expose the status filter" naturally supplies exposed: true and often a label, but has no particular reason to also invent an identifier unless told to - yet the identifier is exactly the field that makes an exposed filter usable at all.

Steps to reproduce

  1. Add a filter handler to a View.
  2. Call ai_agents_views:update_view_handler_option with options: {"exposed":true,"expose":{"label":"Active"}} - no identifier key.
  3. Load the View and inspect the filter's saved expose.identifier, or run $view->validate().

Expected: the identifier defaults to something usable (the field ID), and the View validates cleanly.

Actual (before this fix): expose.identifier is saved as an empty string, and validate() reports "The identifier is required if the filter is exposed."

Root cause

execute() merged the LLM-supplied options with the handler's existing configuration and saved the result directly, with no post-processing:

$view_executable->setHandler($display_id, $handler_type, $field_id, $options + $configuration);

If $options sets exposed: true and an expose array without an identifier key, the merged result simply has no identifier. Drupal core applies no default here on save; the identifier is only auto-filled by FilterPluginBase::defaultExposeOptions() when a handler is first exposed through the Views UI form, a code path this tool never goes through.

Proposed resolution

After merging, default the identifier to the handler's own field ID whenever the filter is exposed and no identifier was supplied - matching Drupal core's own default for a newly exposed handler:

$final_options = $options + $configuration;
if (($final_options['exposed'] ?? FALSE) && ($final_options['expose']['identifier'] ?? '') === '') {
  // An exposed handler with no identifier has no usable query
  // parameter and fails View validation, so default it to the field ID.
  $final_options['expose']['identifier'] = $field_id;
}

$view_executable->setHandler($display_id, $handler_type, $field_id, $final_options);

An explicitly supplied identifier is left untouched - this only fills the gap when the LLM omitted it entirely.

Remaining tasks

  • File this issue on drupal.org.
  • Open an MR with the fix above.

Note: A follow-up issue was found that this same identifier default, combined with a separate shallow-merge behaviour in this method, could silently overwrite an already-set custom identifier during a later partial edit - filed and fixed separately, since it's a distinct defect with its own root cause.

Test coverage added locally

Regression test testExposingFilterWithoutIdentifierDefaultsToFieldId() in tests/src/Kernel/Plugin/AiFunctionCall/UpdateViewHandlerOptionsTest.php exposes a filter with a label but no identifier, and asserts both that the saved identifier equals the field ID and that View::validate() no longer reports the missing-identifier error.

Full suite with this fix applied (isolated on its own branch): 10/10 kernel tests passing (79 assertions) in UpdateViewHandlerOptionsTest, no failures.

API changes

None. This is a bug fix to existing, already-broken behaviour - no new context parameters, no signature changes, no config schema changes.

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

jibran created an issue. See original summary.

jibran’s picture

Status: Active » Needs review

  • jibran committed dbf947d0 on 1.0.x
    fix: #3621943 Fix exposed filters saving with no identifier
    
jibran’s picture

Status: Needs review » Fixed

Committed and pushed to 1.0.x

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.