You are here

public function FacetForm::form in Facets 8

Gets the actual form array to be built.

Overrides EntityForm::form

See also

\Drupal\Core\Entity\EntityForm::processForm()

\Drupal\Core\Entity\EntityForm::afterBuild()

File

src/Form/FacetForm.php, line 145

Class

FacetForm
Provides a form for configuring the processors of a facet.

Namespace

Drupal\facets\Form

Code

public function form(array $form, FormStateInterface $form_state) {
  $form['#attached']['library'][] = 'facets/drupal.facets.admin_css';

  /** @var \Drupal\facets\FacetInterface $facet */
  $facet = $this->entity;
  $facet_sources = [];
  foreach ($this->facetSourcePluginManager
    ->getDefinitions() as $facet_source_id => $definition) {
    $facet_sources[$definition['id']] = !empty($definition['label']) ? $definition['label'] : $facet_source_id;
  }
  if (isset($facet_sources[$facet
    ->getFacetSourceId()])) {
    $form['facet_source'] = [
      '#type' => 'item',
      '#title' => $this
        ->t('Facet source'),
      '#markup' => $facet_sources[$facet
        ->getFacetSourceId()],
    ];
  }
  $widget_options = [];
  foreach ($this->widgetPluginManager
    ->getDefinitions() as $widget_id => $definition) {
    $widget_options[$widget_id] = !empty($definition['label']) ? $definition['label'] : $widget_id;
  }

  // Filters all the available widgets to make sure that only those that
  // this facet applies for are enabled.
  foreach ($widget_options as $widget_id => $label) {
    $widget = $this->widgetPluginManager
      ->createInstance($widget_id);
    if (!$widget
      ->supportsFacet($facet)) {
      unset($widget_options[$widget_id]);
    }
  }
  unset($widget_id, $label, $widget);
  $widget = $facet
    ->getWidgetInstance();
  $form['widget'] = [
    '#type' => 'radios',
    '#title' => $this
      ->t('Widget'),
    '#description' => $this
      ->t('The widget used for displaying this facet.'),
    '#options' => $widget_options,
    '#default_value' => $facet
      ->getWidget()['type'],
    '#required' => TRUE,
    '#ajax' => [
      'trigger_as' => [
        'name' => 'widget_configure',
      ],
      'callback' => '::buildAjaxWidgetConfigForm',
      'wrapper' => 'facets-widget-config-form',
      'method' => 'replace',
      'effect' => 'fade',
    ],
  ];
  $form['widget_config'] = [
    '#type' => 'container',
    '#attributes' => [
      'id' => 'facets-widget-config-form',
    ],
    '#tree' => TRUE,
  ];
  $form['widget_configure_button'] = [
    '#type' => 'submit',
    '#name' => 'widget_configure',
    '#value' => $this
      ->t('Configure widget'),
    '#limit_validation_errors' => [
      [
        'widget',
      ],
    ],
    '#submit' => [
      '::submitAjaxWidgetConfigForm',
    ],
    '#ajax' => [
      'callback' => '::buildAjaxWidgetConfigForm',
      'wrapper' => 'facets-widget-config-form',
    ],
    '#attributes' => [
      'class' => [
        'js-hide',
      ],
    ],
  ];
  $this
    ->buildWidgetConfigForm($form, $form_state);

  // Retrieve lists of all processors, and the stages and weights they have.
  if (!$form_state
    ->has('processors')) {
    $all_processors = $facet
      ->getProcessors(FALSE);
    $sort_processors = function (ProcessorInterface $a, ProcessorInterface $b) {
      return strnatcasecmp((string) $a
        ->getPluginDefinition()['label'], (string) $b
        ->getPluginDefinition()['label']);
    };
    uasort($all_processors, $sort_processors);
  }
  else {
    $all_processors = $form_state
      ->get('processors');
  }
  $enabled_processors = $facet
    ->getProcessors(TRUE);

  // Filters all the available processors to make sure that only those that
  // this facet applies for are enabled.
  foreach ($all_processors as $processor_id => $processor) {
    if (!$processor
      ->supportsFacet($facet)) {
      unset($all_processors[$processor_id]);
    }
  }
  unset($processor_id, $processor);
  $stages = $this->processorPluginManager
    ->getProcessingStages();
  $processors_by_stage = [];
  foreach ($stages as $stage => $definition) {
    foreach ($facet
      ->getProcessorsByStage($stage, FALSE) as $processor_id => $processor) {
      if ($processor
        ->supportsFacet($facet)) {
        $processors_by_stage[$stage][$processor_id] = $processor;
      }
    }
    unset($processor_id, $processor);
  }
  $form['#tree'] = TRUE;
  $form['#attached']['library'][] = 'facets/drupal.facets.index-active-formatters';
  $form['#title'] = $this
    ->t('Edit %label facet', [
    '%label' => $facet
      ->label(),
  ]);

  // Add the list of all other processors with checkboxes to enable/disable
  // them.
  $form['facet_settings'] = [
    '#type' => 'fieldset',
    '#title' => $this
      ->t('Facet settings'),
    '#attributes' => [
      'class' => [
        'search-api-status-wrapper',
      ],
    ],
  ];
  foreach ($all_processors as $processor_id => $processor) {
    if (!$processor instanceof SortProcessorInterface && !$processor instanceof UrlProcessorInterface) {
      $default_value = $processor
        ->isLocked() || $widget
        ->isPropertyRequired($processor_id, 'processors') || !empty($enabled_processors[$processor_id]);
      $clean_css_id = Html::cleanCssIdentifier($processor_id);
      $form['facet_settings'][$processor_id]['status'] = [
        '#type' => 'checkbox',
        '#title' => (string) $processor
          ->getPluginDefinition()['label'],
        '#default_value' => $default_value,
        '#description' => $processor
          ->getDescription(),
        '#attributes' => [
          'class' => [
            'search-api-processor-status-' . $clean_css_id,
          ],
          'data-id' => $clean_css_id,
        ],
        '#disabled' => $processor
          ->isLocked() || $widget
          ->isPropertyRequired($processor_id, 'processors'),
        '#access' => !$processor
          ->isHidden(),
      ];
      $form['facet_settings'][$processor_id]['settings'] = [];
      $processor_form_state = SubformState::createForSubform($form['facet_settings'][$processor_id]['settings'], $form, $form_state);
      $processor_form = $processor
        ->buildConfigurationForm($form, $processor_form_state, $facet);
      if ($processor_form) {
        $form['facet_settings'][$processor_id]['settings'] = [
          '#type' => 'details',
          '#title' => $this
            ->t('%processor settings', [
            '%processor' => (string) $processor
              ->getPluginDefinition()['label'],
          ]),
          '#open' => TRUE,
          '#attributes' => [
            'class' => [
              'facets-processor-settings-' . Html::cleanCssIdentifier($processor_id),
              'facets-processor-settings-facet',
              'facets-processor-settings',
            ],
          ],
          '#states' => [
            'visible' => [
              ':input[name="facet_settings[' . $processor_id . '][status]"]' => [
                'checked' => TRUE,
              ],
            ],
          ],
        ];
        $form['facet_settings'][$processor_id]['settings'] += $processor_form;
      }
    }
  }

  // Add the list of widget sort processors with checkboxes to enable/disable
  // them.
  $form['facet_sorting'] = [
    '#type' => 'fieldset',
    '#title' => $this
      ->t('Facet sorting'),
    '#attributes' => [
      'class' => [
        'search-api-status-wrapper',
      ],
    ],
  ];
  foreach ($all_processors as $processor_id => $processor) {
    if ($processor instanceof SortProcessorInterface) {
      $default_value = $processor
        ->isLocked() || $widget
        ->isPropertyRequired($processor_id, 'processors') || !empty($enabled_processors[$processor_id]);
      $clean_css_id = Html::cleanCssIdentifier($processor_id);
      $form['facet_sorting'][$processor_id]['status'] = [
        '#type' => 'checkbox',
        '#title' => (string) $processor
          ->getPluginDefinition()['label'],
        '#default_value' => $default_value,
        '#description' => $processor
          ->getDescription(),
        '#attributes' => [
          'class' => [
            'search-api-processor-status-' . $clean_css_id,
          ],
          'data-id' => $clean_css_id,
        ],
        '#disabled' => $processor
          ->isLocked(),
        '#access' => !$processor
          ->isHidden(),
      ];
      $form['facet_sorting'][$processor_id]['settings'] = [];
      $processor_form_state = SubformState::createForSubform($form['facet_sorting'][$processor_id]['settings'], $form, $form_state);
      $processor_form = $processor
        ->buildConfigurationForm($form, $processor_form_state, $facet);
      if ($processor_form) {
        $form['facet_sorting'][$processor_id]['settings'] = [
          '#type' => 'container',
          '#open' => TRUE,
          '#attributes' => [
            'class' => [
              'facets-processor-settings-' . Html::cleanCssIdentifier($processor_id),
              'facets-processor-settings-sorting',
              'facets-processor-settings',
            ],
          ],
          '#states' => [
            'visible' => [
              ':input[name="facet_sorting[' . $processor_id . '][status]"]' => [
                'checked' => TRUE,
              ],
            ],
          ],
        ];
        $form['facet_sorting'][$processor_id]['settings'] += $processor_form;
      }
    }
  }
  $form['facet_settings']['only_visible_when_facet_source_is_visible'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Hide facet when facet source is not rendered'),
    '#description' => $this
      ->t('Only display the facet if the facet source is rendered. If you want to display the facets on other pages too, you need to uncheck this setting.'),
    '#default_value' => $widget
      ->isPropertyRequired('only_visible_when_facet_source_is_visible', 'settings') ?: $facet
      ->getOnlyVisibleWhenFacetSourceIsVisible(),
    '#disabled' => $widget
      ->isPropertyRequired('only_visible_when_facet_source_is_visible', 'settings') ?: 0,
  ];
  $form['facet_settings']['show_only_one_result'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Ensure that only one result can be displayed'),
    '#description' => $this
      ->t('Check this to ensure that only <em>one</em> result at a time can be selected for this facet.'),
    '#default_value' => $widget
      ->isPropertyRequired('show_only_one_result', 'settings') ?: $facet
      ->getShowOnlyOneResult(),
    '#disabled' => $widget
      ->isPropertyRequired('show_only_one_result', 'settings') ?: 0,
  ];
  $form['facet_settings']['url_alias'] = [
    '#type' => 'textfield',
    '#title' => $this
      ->t('URL alias'),
    '#description' => $this
      ->t('The alias appears in the URL to identify this facet. It cannot be blank. Allowed are only letters, digits and the following characters: dot ("."), hyphen ("-"), underscore ("_"), and tilde ("~").'),
    '#default_value' => $facet
      ->getUrlAlias(),
    '#maxlength' => 50,
    '#required' => TRUE,
  ];
  $form['facet_settings']['show_title'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Show title of facet'),
    '#description' => $this
      ->t('Show the title of the facet through a Twig template'),
    '#default_value' => $facet
      ->get('show_title'),
  ];
  $empty_behavior_config = $facet
    ->getEmptyBehavior();
  $form['facet_settings']['empty_behavior'] = [
    '#type' => 'radios',
    '#title' => $this
      ->t('Empty facet behavior'),
    '#default_value' => $empty_behavior_config['behavior'] ?: 'none',
    '#options' => [
      'none' => $this
        ->t('Do not display facet'),
      'text' => $this
        ->t('Display text'),
    ],
    '#description' => $this
      ->t('Take this action if a facet has no items.'),
    '#required' => TRUE,
  ];
  $form['facet_settings']['empty_behavior_container'] = [
    '#type' => 'container',
    '#states' => [
      'visible' => [
        ':input[name="facet_settings[empty_behavior]"]' => [
          'value' => 'text',
        ],
      ],
    ],
  ];
  $form['facet_settings']['empty_behavior_container']['empty_behavior_text'] = [
    '#type' => 'text_format',
    '#title' => $this
      ->t('Empty text'),
    '#format' => isset($empty_behavior_config['text_format']) ? $empty_behavior_config['text_format'] : 'plain_text',
    '#editor' => TRUE,
    '#default_value' => isset($empty_behavior_config['text_format']) ? $empty_behavior_config['text'] : '',
  ];
  $form['facet_settings']['query_operator'] = [
    '#type' => 'radios',
    '#title' => $this
      ->t('Operator'),
    '#options' => [
      'or' => $this
        ->t('OR'),
      'and' => $this
        ->t('AND'),
    ],
    '#description' => $this
      ->t('AND filters are exclusive and narrow the result set. OR filters are inclusive and widen the result set.'),
    '#default_value' => $facet
      ->getQueryOperator(),
  ];
  $hard_limit_options = [
    3,
    5,
    10,
    15,
    20,
    30,
    40,
    50,
    75,
    100,
    250,
    500,
  ];
  $form['facet_settings']['hard_limit'] = [
    '#type' => 'select',
    '#title' => $this
      ->t('Hard limit'),
    '#default_value' => $facet
      ->getHardLimit(),
    '#options' => [
      0 => $this
        ->t('No limit'),
    ] + array_combine($hard_limit_options, $hard_limit_options),
    '#description' => $this
      ->t('Display no more than this number of facet items.'),
  ];
  if (!$facet
    ->getFacetSource() instanceof SearchApiDisplay) {
    $form['facet_settings']['hard_limit']['#disabled'] = TRUE;
    $form['facet_settings']['hard_limit']['#description'] .= '<br />';
    $form['facet_settings']['hard_limit']['#description'] .= $this
      ->t('This setting only works with Search API based facets.');
  }
  $form['facet_settings']['exclude'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Exclude'),
    '#description' => $this
      ->t('Exclude the selected facets from the search result instead of restricting it to them.'),
    '#default_value' => $facet
      ->getExclude(),
  ];
  $form['facet_settings']['use_hierarchy'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Use hierarchy'),
    '#default_value' => $facet
      ->getUseHierarchy(),
  ];
  if (!$facet
    ->getFacetSource() instanceof SearchApiDisplay) {
    $form['facet_settings']['use_hierarchy']['#disabled'] = TRUE;
    $form['facet_settings']['use_hierarchy']['#description'] = $this
      ->t('This setting only works with Search API based facets.');
  }
  else {
    $processor_url = Url::fromRoute('entity.search_api_index.processors', [
      'search_api_index' => $facet
        ->getFacetSource()
        ->getIndex()
        ->id(),
    ]);
    $description = $this
      ->t('Renders the items using hierarchy. Make sure to enable the hierarchy processor on the <a href=":processor-url">Search api index processor configuration</a> for this to work as expected. If disabled all items will be flatten.', [
      ':processor-url' => $processor_url
        ->toString(),
    ]);
    $form['facet_settings']['use_hierarchy']['#description'] = $description;
    $form['facet_settings']['use_hierarchy']['#description'] .= '<br />';
    $form['facet_settings']['use_hierarchy']['#description'] .= '<strong>At this moment only hierarchical taxonomy terms are supported.</strong>';
  }
  $form['facet_settings']['keep_hierarchy_parents_active'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Keep hierarchy parents active'),
    '#description' => $this
      ->t('Keep the parents active when selecting a child.'),
    '#default_value' => $facet
      ->getKeepHierarchyParentsActive(),
    '#states' => [
      'visible' => [
        ':input[name="facet_settings[use_hierarchy]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['facet_settings']['expand_hierarchy'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Always expand hierarchy'),
    '#description' => $this
      ->t('Render entire tree, regardless of whether the parents are active or not.'),
    '#default_value' => $facet
      ->getExpandHierarchy(),
    '#states' => [
      'visible' => [
        ':input[name="facet_settings[use_hierarchy]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['facet_settings']['enable_parent_when_child_gets_disabled'] = [
    '#type' => 'checkbox',
    '#title' => $this
      ->t('Enable parent when child gets disabled'),
    '#description' => $this
      ->t('Uncheck this if you want to allow de-activating an entire hierarchical trail by clicking an active child.'),
    '#default_value' => $facet
      ->getEnableParentWhenChildGetsDisabled(),
    '#states' => [
      'visible' => [
        ':input[name="facet_settings[use_hierarchy]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['facet_settings']['min_count'] = [
    '#type' => 'number',
    '#title' => $this
      ->t('Minimum count'),
    '#default_value' => $facet
      ->getMinCount(),
    '#description' => $this
      ->t('Only display the results if there is this minimum amount of results.'),
    '#maxlength' => 4,
    '#required' => TRUE,
  ];
  if (!$facet
    ->getFacetSource() instanceof SearchApiDisplay) {
    $form['facet_settings']['min_count']['#disabled'] = TRUE;
    $form['facet_settings']['min_count']['#description'] .= '<br />';
    $form['facet_settings']['min_count']['#description'] .= $this
      ->t('This setting only works with Search API based facets.');
  }
  $form['facet_settings']['weight'] = [
    '#type' => 'number',
    '#title' => $this
      ->t('Weight'),
    '#default_value' => $facet
      ->getWeight(),
    '#description' => $this
      ->t('This weight is used to determine the order of the facets in the URL if pretty paths are used.'),
    '#maxlength' => 4,
    '#required' => TRUE,
  ];
  $form['weights'] = [
    '#type' => 'details',
    '#title' => $this
      ->t('Advanced settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  ];
  $form['weights']['order'] = [
    '#markup' => $this
      ->t('Processor order'),
    '#prefix' => '<h3>',
    '#suffix' => '</h3>',
  ];

  // Order enabled processors per stage, create all the containers for the
  // different stages.
  foreach ($stages as $stage => $description) {
    $form['weights'][$stage] = [
      '#type' => 'fieldset',
      '#title' => $description['label'],
      '#attributes' => [
        'class' => [
          'search-api-stage-wrapper',
          'search-api-stage-wrapper-' . Html::cleanCssIdentifier($stage),
        ],
      ],
    ];
    $form['weights'][$stage]['order'] = [
      '#type' => 'table',
    ];
    $form['weights'][$stage]['order']['#tabledrag'][] = [
      'action' => 'order',
      'relationship' => 'sibling',
      'group' => 'search-api-processor-weight-' . Html::cleanCssIdentifier($stage),
    ];
  }
  $processor_settings = $facet
    ->getProcessorConfigs();

  // Fill in the containers previously created with the processors that are
  // enabled on the facet.
  foreach ($processors_by_stage as $stage => $processors) {

    /** @var \Drupal\facets\Processor\ProcessorInterface $processor */
    foreach ($processors as $processor_id => $processor) {
      $weight = isset($processor_settings[$processor_id]['weights'][$stage]) ? $processor_settings[$processor_id]['weights'][$stage] : $processor
        ->getDefaultWeight($stage);
      if ($processor
        ->isHidden()) {
        $form['processors'][$processor_id]['weights'][$stage] = [
          '#type' => 'value',
          '#value' => $weight,
        ];
        continue;
      }
      $form['weights'][$stage]['order'][$processor_id]['#attributes']['class'][] = 'draggable';
      $form['weights'][$stage]['order'][$processor_id]['#attributes']['class'][] = 'search-api-processor-weight--' . Html::cleanCssIdentifier($processor_id);
      $form['weights'][$stage]['order'][$processor_id]['#weight'] = $weight;
      $form['weights'][$stage]['order'][$processor_id]['label']['#plain_text'] = (string) $processor
        ->getPluginDefinition()['label'];
      $form['weights'][$stage]['order'][$processor_id]['weight'] = [
        '#type' => 'weight',
        '#title' => $this
          ->t('Weight for processor %title', [
          '%title' => (string) $processor
            ->getPluginDefinition()['label'],
        ]),
        '#title_display' => 'invisible',
        '#default_value' => $weight,
        '#parents' => [
          'processors',
          $processor_id,
          'weights',
          $stage,
        ],
        '#attributes' => [
          'class' => [
            'search-api-processor-weight-' . Html::cleanCssIdentifier($stage),
          ],
        ],
      ];
    }
  }

  // Add vertical tabs containing the settings for the processors. Tabs for
  // disabled processors are hidden with JS magic, but need to be included in
  // case the processor is enabled.
  $form['processor_settings'] = [
    '#title' => $this
      ->t('Processor settings'),
    '#type' => 'vertical_tabs',
  ];
  return $form;
}