1

Goal - How to filter autocomplete based on a related field? In this domain, an organisation is selected and then the autocomplete should only allow items created by that organisation to be selected.

The setup involves an

  • inline entity form to select the organisation. This is the triggering field.
  • auto complete field to select a related bond by that organisation

The triggering element is called 'issuer'. This is the form structure:

Issuer IEF form structure

The autocomplete field structure in the form is:

Deal Bond form structure

Forgetting about how the ajax is triggered when the inline entity form completes as another problem. I have a system where a fake/triggering select list is changed based on a mutation observer triggering the ajax. I have investigated trying to chain the event, trying to apply the ajax to the inline entity form element and trying to call the ajax using drupal.ajax.

How do I set the filter value id. I can see 2 ways, the cleanest is passing a #filter parameter to #selection_settings

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_settings'] = [
  'target_bundles' => ['bean'],
  'filter' => ['field_bond_issuer' => $issuer_id],
];

I can see the value set in a #prefix element on the form after the ajax has run

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
return $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond'];

The field appearance:

enter image description here

The html

<input data-drupal-selector="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id" aria-describedby="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id--_drZ4Yf-StI--description" class="form-autocomplete form-text ui-autocomplete-input" data-autocomplete-path="/entity_reference_autocomplete/node/default%3Anode_by_issuer/tT7cuAtHgjMlRrK6OypcvFEJNTvFEDSmFImi6iBhNNw" type="text" id="edit-field-para-deal-information-0-subform-field-deal-reference-bond-0-target-id--_drZ4Yf-StI" name="field_para_deal_information[0][subform][field_deal_reference_bond][0][target_id]" value="" size="60" maxlength="1024" placeholder="" autocomplete="off">

Shows that default:node_by_issuer is set in the ajax function (full):

function cbi_deal_id_handle_ajax(array &$form, FormStateInterface $form_state) {
  $issuer_para_field = $form_state->getValue('field_para_issuer_information');
  $issuer_field = $issuer_para_field[0]['subform']['field_bond_issuer'];
  $issuer_field_value = $issuer_field['target_id'];
  $issuer_id = explode(':', $issuer_field_value)[1];


  $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_handler'] = 'default:node_by_issuer';
  $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#selection_settings'] = [
'target_bundles' => ['bean'],
'filter' => ['field_bond_issuer' => $issuer_id],
  ];

 $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
 return $form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond'];
 }

I break at Review at /web/core/lib/Drupal/Core/Render/Element/FormElement.php line 187 and confirm the filter variable is not set.

It appears that the autocomplete field is not being updated with settings relating to the autocomplete function. If I set the ajax to update the form with

['#selection_handler'] = 'default:node_by_tester';

there is no change to the handler in the core renderer. The value set by the initial form alter remains (node_by_issuer).

The ajax form alter, with wrapping div to test if the form wrapper id with the hash-code on the end makes any difference

function cbi_deal_id_field_widget_entity_reference_paragraphs_form_alter(&$element, &$form_state, $context) {
  if ($element['#paragraph_type'] == 'deal_information') {
    $items = $context['items'];
    $bond = $items->getParent()->getEntity();
    $element['subform']['field_deal_reference_bond']['widget'][0]['target_id']['#selection_handler'] = 'default:node_by_issuer';
    $element['subform']['field_deal_reference_bond']['#prefix'] = '<div id="test-wrapper">';
    $element['subform']['field_deal_reference_bond']['#suffix'] = '</div>';

    $element['subform']['ajax_trigger_button'] = [
      '#type' => 'select',
      '#ajax' => [
        'callback' => 'cbi_deal_id_handle_ajax',
        'event' => 'blur',
        'wrapper' => 'test-wrapper',
        'progress' => [
          'type' => 'throbber',
          'message' => t('Loading issuer details ...'),
         ],
      ],
     '#options' => [
      '1' => t('One'),
      '2' => t('Two'),
      '3' => t('Three'),
     ],
   ];

  if ($bond->isNew()) {
    return;
  }
...

In order to filter the data I've created a plugin as per https://fivejars.com/blog/when-drupal-best-choice-your-website. I'm expecting that the selection_handler property addresses it. This is being called, without access to the filter data:

<?php

namespace Drupal\cbi_deal_id\Plugin\EntityReferenceSelection;

use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;

/**
 * Provides specific access control for the node entity type.
 *
 * @EntityReferenceSelection(
 *   id = "default:node_by_issuer",
 *   label = @Translation("Node by field selection"),
 *   entity_types = {"node"},
 *   group = "default",
 *   weight = 3
 * )
 */
class BondByIssuerSelection extends NodeSelection {

 /**
  * {@inheritdoc}
  */
 protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
   \Drupal::logger('cbi_deal_id')->notice('loading build entity query with');
   $query = parent::buildEntityQuery($match, $match_operator);
   $handler_settings = $this->configuration['handler_settings'];
   if (!isset($handler_settings['filter'])) {
     return $query;
   }
   $filter_settings = $handler_settings['filter'];
   foreach ($filter_settings as $field_name => $value) {
     $query->condition($field_name, $value, '=');
   }
   return $query;
 }

}

Ajax returning the entire paragraph

Adjust the ajax to replace the entire paragraph as suggested

$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['target_id']['#selection_settings'] = 
  [
     'target_bundles' => ['bond'],
     'filter' => ['field_issuer_filter' => 4993],
   ];
$form['field_para_deal_information']['widget'][0]['subform']['field_deal_reference_bond']['#prefix'] = 'testing add content: issuer id' . $issuer_id;
  return $form['field_para_deal_information'];
}

In the matcher file /src/Entity/CbiDealIdEntityAutocompleteMatcher.php

The filter settings are in the autocomplete matcher controller, but not the updated values

enter image description here

Conclusion

I have tried creating a controller https://www.chapterthree.com/blog/how-alter-entity-autocomplete-results-drupal-8. It does get called, but I have no filter data and cannot see how to address the $form_state to get the id from the inline entity form.

The second method of addressing the query is via a custom route. Possibly can change the route from ajax. Investigation required.

What is the simplest method of filtering by the value in an inline entity field?

1
  • 1
    In your ajax callback, you need to just return $form['field_para_deal_information'];
    – No Sssweat
    Nov 29 '21 at 7:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.