CCK Field Type with Several Form Fields

Specs

Version
Drupal 8
Tools
Field API
Created
08 Nov 2015

Summary

This example implements a field type that can be used to store code snippets. It stores the following information that should be used as a multi-value field with unlimited values.

  • Section Name
  • File Name
  • File Path
  • Language
  • Description
  • Code

This is actually the field type I implemented for the snippets on this site. Eventually, I plan on writing functionality that will automatically generate modules and Drush commands from the data.

Code

1. Module File

MODULE_NAME.module
Basic module file that stores options for the field.
          
/**
 * @file
 * Module file for MODULE_NAME.
 */

use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\image\Entity\ImageStyle;
use Drupal\core\Entity;
use Drupal\Core\Url;

define('MODULE_NAME_INCLUDE', 1);
define('MODULE_NAME_NO_INCLUDE', 2);

/*
 * Stores language options
 */
function MODULE_NAME_language_options() {
  return array(
    'php' => t('PHP'),
    'js' => t('JS'),
    'css' => t('CSS'),
    'html' => t('HTML'),
    'yaml' => t('YAML'),
  );
}

/*
 * Stores linker include options
 */
function MODULE_NAME_include_options() {
  return array(
    MODULE_NAME_INCLUDE => t('Include in Linker'),
    MODULE_NAME_NO_INCLUDE => t('Do not include in Linker'),
  );
}
          
          

2. CCK Field Type Plugin

CodeItem.php
MODULE_PATH/src/Plugin/Field/FieldType
Defines the code field type.
          
/**
 * @file
 * Contains \Drupal\MODULE_NAME\Plugin\Field\FieldType\Code.
 */

namespace Drupal\MODULE_NAME\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;

/**
 * Plugin implementation of the 'MODULE_NAME' field type.
 *
 * @FieldType(
 *   id = "code",
 *   label = @Translation("Code"),
 *   description = @Translation("This field is used to store sections of code."),
 *   category = @Translation("Text"),
 *   default_widget = "code",
 *   default_formatter = "code_default"
 * )
 */
class CodeItem extends FieldItemBase {

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {

    $properties['value'] = DataDefinition::create('string')
      ->setLabel(t('Code'))
      ->setRequired(TRUE);

      $properties['section_name'] = DataDefinition::create('string')
      ->setLabel(t('Section Name'))
      ->setRequired(TRUE);

    $properties['file_name'] = DataDefinition::create('string')
      ->setLabel(t('File Name'));

    $properties['file_path'] = DataDefinition::create('string')
      ->setLabel(t('File Path'));

    $properties['language'] = DataDefinition::create('string')
      ->setLabel(t('Language'));

    $properties['description'] = DataDefinition::create('string')
      ->setLabel(t('Description'));

    $properties['include'] = DataDefinition::create('string')
      ->setLabel(t('Include'));

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return array(
      'columns' => array(
        'value' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'section_name' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'file_name' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'file_path' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'language' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'description' => array(
          'type' => 'text',
          'size' => 'big',
        ),
        'include' => array(
          'type' => 'int',
        ),
      ),
    );
  }

  /**
  * {@inheritdoc}
  */
  public function isEmpty() {
    $value = $this->get('value')->getValue();
    return $value === NULL || $value === '';
  }

}

          
          

3. CCK Field Widget

CodeWidget.php
MODULE_PATH/src/Plugin/Field/FieldWidget
Defines the code field widget.
          
/**
 * @file
 * Contains \Drupal\MODULE_NAME\Plugin\Field\FieldWidget\TextareaWidget.
 */

namespace Drupal\MODULE_NAME\Plugin\Field\FieldWidget;

use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;

/**
 * Plugin implementation of the 'code' widget.
 *
 * @FieldWidget(
 *   id = "code",
 *   label = @Translation("Code Field Widget"),
 *   field_types = {
 *     "code"
 *   }
 * )
 */
class CodeWidget extends WidgetBase {

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {

    $element['section_name'] = array(
      '#type' => 'textfield',
      '#default_value' => $items[$delta]->section_name,
      '#title' => t('Section Name'),
      '#attached' => array(
        'library' => array('text/drupal.text'),
      ),
    );

    $element['file_name'] = array(
      '#type' => 'textfield',
      '#default_value' => $items[$delta]->file_name,
      '#title' => t('File Name'),
      '#attached' => array(
        'library' => array('text/drupal.text'),
      ),
    );

    $element['file_path'] = array(
      '#type' => 'textfield',
      '#default_value' => $items[$delta]->file_path,
      '#title' => t('File Path'),
      '#attached' => array(
        'library' => array('text/drupal.text'),
      ),
    );

    $element['language'] = array(
      '#type' => 'radios',
      '#default_value' => !empty($items[$delta]->language) ? $items[$delta]->language : 'php',
      '#title' => t('Language'),
      '#options' => MODULE_NAME_language_options(),
    );

    $element['include'] = array(
      '#type' => 'radios',
      '#default_value' => !empty($items[$delta]->include) ? $items[$delta]->include : MODULE_NAME_INCLUDE,
      '#title' => t('Linker Include Method'),
      '#options' => MODULE_NAME_include_options(),
    );

    $element['description'] = array(
      '#type' => 'textarea',
      '#default_value' => $items[$delta]->description,
      '#title' => t('Description'),
      '#rows' => 4,
      '#attached' => array(
        'library' => array('text/drupal.text'),
      ),
    );

    $element['value'] = array(
      '#type' => 'textarea',
      '#default_value' => $items[$delta]->value,
      '#title' => t('Code'),
      '#rows' => 20,
      '#attached' => array(
        'library' => array('text/drupal.text'),
      ),
      '#resizeable' => TRUE,
    );

    return $element;
  }

}

          
          

Comments

Placeholder: I'll extend node comments with ajax and other functionality here.