Custom content type with custom fields for your Drupal 8 module

David - Touch4IT
Dávid Ondruš
Nov 13, 2018
5 min read
drupal 8

We were developing a module that downloads content from 3rd party API and saves it as content in Drupal. We had to follow the structure of data but there was no suitable content type on Drupal website so We’ve created our own.

Despite the fact that creation of custom content type is documented in official Drupal documentation, it lacks many explanations of what that lines really do and is built on one specific example without providing another options. We decided to write down findings from our trial-and-error practises and also pieces of information collected from a bunch of tutorials, websites, etc. We will try to explain some entries in yml files.

How to begin?

To start creating custom content type for Drupal you have to create a module. Since content type will be installed together with module when you enable the module on “Extend” page, all project *.yml configuration files must be placed into “my_module/config/install” where “my_module” is an identifier of your module. “Content” is represented programatically as “node”, so we will basically create new “node type”. For simplicity, I will use “foo” as the name.

1 file is needed as the main config file, 2 new files and 2 edited files are needed for each field of the content type. Only proper configuration guarantees that content type will be properly uninstalled with module. Otherwise it won’t be possible to install it again because of leftovers from previous installation.

 

Main config file (node.type.foo.yml)


langcode: en
dependencies:
  enforced:
    module:
    - my_module
name: 'Foo'
type: foo
description: 'Test content type description.'
help: 'Test help text.'
new_revision: false
preview_mode: 2

 

This file describes the content type. Enforced dependency on “my_module” links this content type to the module and so it will be uninstallled during module unistallation.
“name”, “description” — displayed in UI
“type” — identifier (3rd string in file name)
“help” — displayed on “Add content” screen
“new_revision” — if “true”, new revision will be created with every update of the content (“Create new revision” will be checked in UI)
“preview_mode” — 0 removes “Preview” button while creating content, 1 is default with “Save” and “Preview” buttons, 2 requires to first “Preview” content and then “Save” button will appear

 

drupal 8 content type name description

 

drupal 8 content type help text

 

drupal 8 content type revision

 

Storage (field.storage.node.field_foo_id.yml) — new


langcode: en
dependencies:
  enforced:
    module:
    - my_module
  module:
  - node
id: node.field_foo_id
field_name: field_foo_id
entity_type: node
type: string
settings:
  max_length: 10
module: core
locked: false
cardinality: 1


This file describes the storage behind the field. Again, enforced dependency ensures it will be uninstalled properly. List of non-enforced dependencies consists of “node” and a module name according to the “type” of the field (possible values are e.g. “text”, “taxonomy”, “datetime”, “link”, “paragraphs”, “address”, “file”, “image”, …) — these two don’t have to match.
“id” — concatenation of 3rd and 4th strings of file name
“field_name” — 4th string of file name
“type” —Drupal’s data type of storage, possible values are e.g. “text_with_summary”, “string”, “integer”, “datetime”, “text_long”, “entity_reference”, “link”, “image”, “address”, “text”, …
“settings” — depends on the “type”, it’s and array of “Storage settings” available when creating Content type in UI, also can be an empty array (value “{ }”)
“module” — an entry from non-enforced dependencies — if the field doesn’t depend on any module (e.g. it’s a primitive data type), this value will be “core”
“locked” — enables or disables changing field settings after installation (if a module is just a downloader of data saving it to specific content type it’s good practice to lock it)
“cardinality” — how many values can be stored (e.g. 1 for single string, ≥2 for array of strings)

 

drupal 8 content type storage settings

 

drupal 8 content type locked

 

Field (field.field.node.foo.field_foo_id.yml) — new


langcode: en
dependencies:
  config:
  - field.storage.node.field_foo_id
  - node.type.foo
id: node.foo.field_foo_id
field_name: field_foo_id
entity_type: node
bundle: foo
label: 'Foo ID'
description: 'ID field description'
required: false
default_value: 'example'
settings: { }
field_type: string


This file adds some more field settings. Config dependencies are previous 2 files. Also, there can be module dependencies but as stated above, string is a primitive type so it depends only on “core”. In another case, possible values are identical with the ones in field storage file above (except “node”).
“id” — concatenation of 3rd-5th strings of file name
“field_name” — machine translatable name, so basically an identifier (5th string of file name)
“bundle” — 4th string of file name
“label”, “description” —displayed in UI
“required” —if true, value must be entered before saving the entity
“default_value” — if no value is specified for this field, this value will be used (and also displayed by default in UI form)
“settings” — depends on the type
“field_type” — equals to “type” in the previous file

 

drupal 8 content type default value

 

View (core.entity_view_display.node.foo.default.yml) — edit


langcode: en
status: true
dependencies:
  config:
  - field.field.node.foo.field_foo_id
  - node.type.foo
id: node.foo.default
targetEntityType: node
bundle: foo
mode: default
content:
  field_foo_id:
    label: hidden
    type: string
    weight: 100
    settings: {  }
    third_party_settings: {  }


This file describes how an entity will be displayed on a website. Config dependencies must contain all created fields. “module” array under dependencies (which is not present in my file due to the string type not using any module) must contain all modules used by “type” of fields in “content” array (“text_default”, …).
“status” — enables or disables content view
“id” — concatenation of 3rd-5th strings of file name
“mode” — last string in file name
“content” — array of all fields that should be displayed (keys are fields’ machine readable names)
“label” — if this entry is not present, label will be displayed, if it is and has value “hidden”, label won’t be displayed
“type” —can be different from types in previous files — it’s a formatter used to display field’s value (formatter can be found as “default_formatter” in “core/lib/Drupal/Core/Field/Plugin/Field/FieldType/<class_name>.php”) — api.drupal.org
“weight” — must be different for every entry, determines order
“settings”, “third_party_settings” — depends on “type”

Form (core.entity_form_display.node.foo.default.yml) — edit


langcode: en
status: true
dependencies:
  config:
  - field.field.node.foo.field_foo_id
  - node.type.foo
id: node.foo.default
targetEntityType: node
bundle: foo
mode: default
content:
  field_foo_id:
    type: string_textfield
    weight: 100
    settings:
      size: 30
      placeholder: 'example'
    third_party_settings: {  }
hidden: { }

 

This file describes how a form for creating/editing entity will look. Entries are almost the same as in the previous “view” file but “type” in “content” array here determines field’s widget. Widget can be found as “default_widget” in “core/lib/Drupal/Core/Field/Plugin/Field/FieldType/<class_name>.php”. — api drupal
“hidden” — an array of hidden elements

 

drupal 8 content type placeholder

 

Final file structure with 1 custom field

 

drupal 8 content type file structure

Conclusions and how to make it easier

It’s good to know what that lines in *.yml configuration files really mean, do and what are they options. But still, the process of creating fields for your content type manually can be really difficult. Cool trick is to use the UI to create a dummy content type with all fields you need and then export configurations through Drupal configuration menu at “/admin/config/development/configuration/single/export”. Then you can create files from them in your custom module’s project.