Mutiple fields of different types in a custom field
This example showcases a Custom field that's composed of multiple fields that are of different types. Specifally: textfields, numbers and formatted text
FieldType
The FieldType is the class that will handle the storage of the field. It will be responsible for storing the data in the database and retrieving it. It will also be responsible for validating the data.
When creating the schema, there are some rules you should follow:
textfields should be of type text and the size shoul be tiny
Numbers should be of type int and the size should be normal
Formatted Text should be of type text and the size should be big
<?phpnamespaceDrupal\MODULE_NAME\Plugin\Field\FieldType;useDrupal\Core\Field\FieldItemBase;useDrupal\Core\Field\FieldStorageDefinitionInterface;useDrupal\Core\TypedData\DataDefinition;/** * @FieldType( * id = "YOUR_FIELD_TYPE_ID", * label = @Translation("THE LABEL THAT WILL BE DISPLAYED"), * description = @Translation("A SIMPLE DESCRIPTION OF THE FIELD"), * category = @Translation("THE CATEGORY WHERE IT WILL BE DISPLAYED"), * default_widget = "YOUR_DEFAULT_WIDGET", * default_formatter = "YOUR_DEFAULT_FORMATTER", * ) */classFieldTypeNameextendsFieldItemBase {publicstaticfunctionpropertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['FIELD_ONE'] =DataDefinition::create('string')->setLabel(t('FIELD_ONE')); $properties['FIELD_TWO'] =DataDefinition::create('integer')->setLabel(t('FIELD_TWO')); $properties['FIELD_THREE'] =DataDefinition::create('string')->setLabel(t('FIELD_THREE')); $properties['FIELD_FOUR'] =DataDefinition::create('integer')->setLabel(t('FIELD_FOUR')); $properties['FIELD_FIVE'] =DataDefinition::create('string')->setLabel(t('FIELD_FIVE')); $properties['FIELD_SIX'] =DataDefinition::create('string')->setLabel(t('FIELD_SIX')); $properties['FIELD_SEVEN'] =DataDefinition::create('integer')->setLabel(t('FIELD_SEVEN'));return $properties; }publicstaticfunctionschema(FieldStorageDefinitionInterface $field_definition) {// Create a table with three columns. one for a text field, one for a number// field, and one for a long text field.return ['columns'=> ['FIELD_ONE'=> ['type'=>'text','size'=>'tiny','not null'=>FALSE, ],'FIELD_TWO'=> ['type'=>'int','size'=>'normal','not null'=>FALSE, ],'FIELD_THREE'=> ['type'=>'text','size'=>'tiny','not null'=>FALSE, ],'FIELD_FOUR'=> ['type'=>'int','size'=>'normal','not null'=>FALSE, ],'FIELD_FIVE'=> ['type'=>'text','size'=>'tiny','not null'=>FALSE, ],'FIELD_SIX'=> ['type'=>'text','size'=>'big','not null'=>FALSE, ],'FIELD_SEVEN'=> ['type'=>'int','size'=>'normal','not null'=>FALSE, ], ], ]; }publicfunctionisEmpty() { $FIELD_ONE =$this->get('FIELD_ONE')->getValue(); $FIELD_THREE =$this->get('FIELD_THREE')->getValue(); $FIELD_SIX =$this->get('FIELD_SIX')->getValue();if (empty($FIELD_ONE)&&empty($FIELD_THREE)&&empty($FIELD_SIX)) {returnTRUE; }returnFALSE; }}
FieldWidget
The FieldWidget is the class that will handle the display of the field in the form. It will be responsible for displaying the field in the form and handling the data that is sent from the form.
When working with formatted text, there are certain rules that you should follow:
The form field should have a type of "text_format"
You should add the "format" you're using (basic_html, full_html, plaintext, etc.)
You should use the massageFormValues , this will take the value that is being sent, which in this case is an array an take only the value we want, if the following method is not implemented, the form won't save any information regarded to this field.
When working with numbers be sure to implement the errorElement method to check for constraints.
<?phpnamespaceDrupal\MODULE_NAME\Plugin\Field\FieldWidget;useDrupal\Core\Field\FieldItemListInterface;useDrupal\Core\Field\WidgetBase;useDrupal\Core\Form\FormStateInterface;/** * @FieldWidget( * id = "YOUR_DEFAULT_WIDGET", * label = @Translation("THE LABEL THAT WILL BE DISPLAYED"), * description = @Translation("A SIMPLE DESCRIPTION OF THE FIELD"), * field_types = { * "YOUR_FIELD_TYPE_ID" * } * ) */classFieldWidgetNameextendsWidgetBase {/** * {@inheritdoc} */publicfunctionformElement(FieldItemListInterface $items, $delta,array $element,array&$form,FormStateInterface $form_state) {// Create form elements for each field in the field type. $element['FIELD_ONE'] = ['#type'=>'textfield','#title'=>t('FIELD_ONE'),'#default_value'=> $items[$delta]->FIELD_ONE ??NULL,'#attributes'=> ['style'=>'width: 100%;', ], ]; $element['FIELD_TWO'] = ['#type'=>'number','#title'=>t('FIELD_TWO'),'#default_value'=> (isset($items[$delta]->FIELD_TWO)&&!empty($items[$delta]->FIELD_TWO)) ? $items[$delta]->FIELD_TWO :0,'#attributes'=> ['style'=>'width: 120px;', ], ]; $element['FIELD_THREE'] = ['#type'=>'textfield','#title'=>t('FIELD_THREE'),'#default_value'=> $items[$delta]->FIELD_THREE ??NULL,'#attributes'=> ['style'=>'width: 100%;', ], ]; $element['FIELD_FOUR'] = ['#type'=>'number','#title'=>t('FIELD_FOUR'),'#default_value'=>isset($items[$delta]->FIELD_FOUR)? $items[$delta]->FIELD_FOUR :0,'#attributes'=> ['style'=>'width: 120px;', ], ]; $element['FIELD_FIVE'] = ['#type'=>'textfield','#title'=>t('FIELD_FIVE'),'#default_value'=> $items[$delta]->FIELD_FIVE ??NULL,'#attributes'=> ['style'=>'width: 100%;', ], ]; $element['FIELD_SIX'] = ['#type'=>'text_format','#title'=>t('FIELD_SIX'),'#format'=>'full_html',// Or any format you want."#base_type"=>"textarea",'#default_value'=>isset($items[$delta]->FIELD_SIX)? $items[$delta]->FIELD_SIX :NULL, ]; $element['FIELD_SEVEN'] = ['#type'=>'number','#title'=>t('FIELD_SEVEN'),'#default_value'=>isset($items[$delta]->FIELD_SEVEN)? $items[$delta]->FIELD_SEVEN :0,'#attributes'=> ['style'=>'width: 100px;', ], ];return $element; }publicfunctionmassageFormValues(array $values,array $form,FormStateInterface $form_state) {foreach ($values as $key => $value) { $values[$key]['FIELD_SIX'] = $value['FIELD_SIX']['value']; }return $values; }/** * {@inheritdoc} */publicfunctionerrorElement(array $element,ConstraintViolationInterface $violation,array $form,FormStateInterface $form_state) {if ( $violation->arrayPropertyPath == ['FIELD_TWO'] &&!is_numeric($element['FIELD_TWO']['#value'])|| $violation->arrayPropertyPath == ['FIELD_FOUR'] &&!is_numeric($element['FIELD_FOUR']['#value'])|| $violation->arrayPropertyPath == ['FIELD_SEVEN'] &&!is_numeric($element['FIELD_SEVEN']['#value']) ) {returnFALSE; }return $element; }}
FieldFormatter
The FieldFormatter is the class that will handle the display of the field in the view. It will be responsible for displaying the field in the view.
<?phpnamespaceDrupal\MODULE_NAME\Plugin\Field\FieldFormatter;useDrupal\Core\Field\FieldItemListInterface;useDrupal\Core\Field\FormatterBase;/** * @FieldWidget( * id = "YOUR_DEFAULT_WIDGET", * label = @Translation("THE LABEL THAT WILL BE DISPLAYED"), * description = @Translation("A SIMPLE DESCRIPTION OF THE FIELD"), * field_types = { * "YOUR_FIELD_TYPE_ID" * } * ) */classFieldFormatterNameextendsFormatterBase {/** * {@inheritdoc} */publicfunctionviewElements(FieldItemListInterface $items, $langcode) { $elements = [];foreach ($items as $delta => $item) {// The text value has no text format assigned to it, so the user input// should equal the output, including newlines. $elements[$delta] = ['#type'=>'inline_template','#template'=>'{{ value|nl2br }}','#context'=> ['value'=> $item->value], ]; }return $elements; }}