/** * Special handling to create form elements for multiple values. * * Handles generic features for multiple fields: * - number of widgets * - AHAH-'add more' button * - table display and drag-n-drop value reordering */ protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getName(); $cardinality = $this->fieldDefinition->getFieldStorageDefinition()->getCardinality(); $parents = $form['#parents']; // Determine the number of widgets to display. switch ($cardinality) { case FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED: $field_state = static::getWidgetState($parents, $field_name, $form_state); $max = $field_state['items_count']; $is_multiple = TRUE; break; default: $max = $cardinality - 1; $is_multiple = $cardinality > 1; break; } $title = $this->fieldDefinition->getLabel(); $description = FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); $elements = array(); for ($delta = 0; $delta <= $max; $delta++) { // Add a new empty item if it doesn't exist yet at this delta. if (!isset($items[$delta])) { $items->appendItem(); } // For multiple fields, title and description are handled by the wrapping // table. if ($is_multiple) { $element = ['#title' => $this->t('@title (value @number)', ['@title' => $title, '@number' => $delta + 1]), '#title_display' => 'invisible', '#description' => '']; } else { $element = ['#title' => $title, '#title_display' => 'before', '#description' => $description]; } $element = $this->formSingleElement($items, $delta, $element, $form, $form_state); if ($element) { // Input field for the delta (drag-n-drop reordering). if ($is_multiple) { // We name the element '_weight' to avoid clashing with elements // defined by widget. $element['_weight'] = array('#type' => 'weight', '#title' => $this->t('Weight for row @number', array('@number' => $delta + 1)), '#title_display' => 'invisible', '#delta' => $max, '#default_value' => $items[$delta]->_weight ?: $delta, '#weight' => 100); } $elements[$delta] = $element; } } if ($elements) { $elements += array('#theme' => 'field_multiple_value_form', '#field_name' => $field_name, '#cardinality' => $cardinality, '#cardinality_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(), '#required' => $this->fieldDefinition->isRequired(), '#title' => $title, '#description' => $description, '#max_delta' => $max); // Add 'add more' button, if not working with a programmed form. if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && !$form_state->isProgrammed()) { $id_prefix = implode('-', array_merge($parents, array($field_name))); $wrapper_id = Html::getUniqueId($id_prefix . '-add-more-wrapper'); $elements['#prefix'] = '<div id="' . $wrapper_id . '">'; $elements['#suffix'] = '</div>'; $elements['add_more'] = array('#type' => 'submit', '#name' => strtr($id_prefix, '-', '_') . '_add_more', '#value' => t('Add another item'), '#attributes' => array('class' => array('field-add-more-submit')), '#limit_validation_errors' => array(array_merge($parents, array($field_name))), '#submit' => array(array(get_class($this), 'addMoreSubmit')), '#ajax' => array('callback' => array(get_class($this), 'addMoreAjax'), 'wrapper' => $wrapper_id, 'effect' => 'fade')); } } return $elements; }
/** * {@inheritdoc} */ public function getDescription() { return $this->fieldDefinition->getDescription(); }
/** * Provides the views data for a given data type and schema field. * * @param string $table * The table of the field to handle. * @param string $field_name * The machine name of the field being processed. * @param string $field_type * The type of field being handled. * @param string $column_name * For fields containing multiple columns, the column name being processed. * @param string $column_type * Within the field, the column type being handled. * @param bool $first * TRUE if this is the first column within the field. * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition * The field definition. * * @return array * The modified views data field definition. */ protected function mapSingleFieldViewsData($table, $field_name, $field_type, $column_name, $column_type, $first, FieldDefinitionInterface $field_definition) { $views_field = array(); // Provide a nicer, less verbose label for the first column within a field. // @todo Introduce concept of the "main" column for a field, rather than // assuming the first one is the main column. if ($first) { $views_field['title'] = $field_definition->getLabel(); } else { $views_field['title'] = $field_definition->getLabel() . " ({$column_name})"; } if ($description = $field_definition->getDescription()) { $views_field['help'] = $description; } // Set up the field, sort, argument, and filters, based on // the column and/or field data type. // @todo Allow field types to customize this. // @see https://www.drupal.org/node/2337515 switch ($field_type) { // Special case a few field types. case 'timestamp': case 'created': case 'changed': $views_field['field']['id'] = 'date'; $views_field['argument']['id'] = 'date'; $views_field['filter']['id'] = 'date'; $views_field['sort']['id'] = 'date'; break; case 'language': $views_field['field']['id'] = 'field'; $views_field['argument']['id'] = 'language'; $views_field['filter']['id'] = 'language'; $views_field['sort']['id'] = 'standard'; break; case 'boolean': $views_field['field']['id'] = 'field'; $views_field['argument']['id'] = 'numeric'; $views_field['filter']['id'] = 'boolean'; $views_field['sort']['id'] = 'standard'; break; case 'uri': // Let's render URIs as URIs by default, not links. $views_field['field']['id'] = 'field'; $views_field['field']['default_formatter'] = 'string'; $views_field['argument']['id'] = 'string'; $views_field['filter']['id'] = 'string'; $views_field['sort']['id'] = 'standard'; break; case 'text': case 'text_with_summary': // Treat these three long text fields the same. $field_type = 'text_long'; // Intentional fall-through here to the default processing! // Intentional fall-through here to the default processing! default: // For most fields, the field type is generic enough to just use // the column type to determine the filters etc. switch ($column_type) { case 'int': case 'integer': case 'smallint': case 'tinyint': case 'mediumint': case 'float': case 'double': case 'decimal': $views_field['field']['id'] = 'field'; $views_field['argument']['id'] = 'numeric'; $views_field['filter']['id'] = 'numeric'; $views_field['sort']['id'] = 'standard'; break; case 'char': case 'string': case 'varchar': case 'tinytext': case 'text': case 'mediumtext': case 'longtext': $views_field['field']['id'] = 'field'; $views_field['argument']['id'] = 'string'; $views_field['filter']['id'] = 'string'; $views_field['sort']['id'] = 'standard'; break; default: $views_field['field']['id'] = 'field'; $views_field['argument']['id'] = 'standard'; $views_field['filter']['id'] = 'standard'; $views_field['sort']['id'] = 'standard'; } } // Do post-processing for a few field types. $process_method = 'processViewsDataFor' . Container::camelize($field_type); if (method_exists($this, $process_method)) { $this->{$process_method}($table, $field_definition, $views_field, $column_name); } return $views_field; }
/** * Returns the filtered field description. * * @return \Drupal\Core\Field\FieldFilteredMarkup * The filtered field description, with tokens replaced. */ protected function getFilteredDescription() { return FieldFilteredMarkup::create(\Drupal::token()->replace($this->fieldDefinition->getDescription())); }