Test page
class PairLookupForm extends FormBase { public function buildForm(array $form, FormStateInterface $form_state) { $options = $this->loadTermOptions(); // tid => label, from pair_options vocab $form['option_a'] = [ '#type' => 'select', '#title' => $this->t('First choice'), '#options' => $options, '#required' => TRUE, ]; $form['option_b'] = [ '#type' => 'select', '#title' => $this->t('Second choice'), '#options' => $options, '#required' => TRUE, ]; $form['actions']['submit'] = ['#type' => 'submit', '#value' => $this->t('Show result')]; return $form; } public function validateForm(array &$form, FormStateInterface $form_state) { if ($form_state->getValue('option_a') == $form_state->getValue('option_b')) { $form_state->setErrorByName('option_b', $this->t('Please choose two different options.')); } } public function submitForm(array &$form, FormStateInterface $form_state) { $a = $form_state->getValue('option_a'); $b = $form_state->getValue('option_b'); $key = implode('-', [min($a, $b), max($a, $b)]); $nids = \Drupal::entityQuery('node') ->condition('type', 'pair_result') ->condition('field_pair_key', $key) ->accessCheck(TRUE) ->execute(); if ($nid = reset($nids)) { $form_state->setRedirect('entity.node.canonical', ['node' => $nid]); } else { $this->messenger()->addWarning($this->t('No result found for that pair yet.')); } } }