Creating custom content entities in Drupal 8/9

Published by Alan Saunders on Wednesday, March 24, 2021 - 22:00

Reading Time: 7 minutes




In this blog post, we are going to look at creating content entities using Drupal console. If you need to read about Drupal console, have a look at:

Drupal console is a really useful tool and certainly saves time and errors. 


First of all we need to have a custom module that we can use to store our code for our custom entity, for this we can use the Drupal console command:

drupal generate:module

Or using the alias:

drupal gm

You should then be asked as a series of questions, below are a note of the questions and my responses. I have touched on some of the questions in previous blog posts, for the purposes of what we need for this blog post, some of the questions that default too yes, I have changed to no, as it is just unnecessary noise that I don't need for this blog post.

$ drupal gm

// Welcome to the Drupal module generator

Enter the new module name:
> custom content entity bundle

Enter the module machine name [custom_content_entity]:

Enter the module Path [modules/custom]:

Enter module description [My Awesome Module]:
> Custom content entity

Enter package name [Custom]:

Enter Drupal Core version [8.x]:

Do you want to generate a .module file? (yes/no) [yes]:
> yes

Define module as feature (yes/no) [no]:

Do you want to add a composer.json file to your module? (yes/no) [yes]:
> no

Would you like to add module dependencies? (yes/no) [no]:
> no

Do you want to generate a unit test class? (yes/no) [yes]:
> no

Do you want to generate a themeable template? (yes/no) [yes]:
> no

Do you want proceed with the operation? (yes/no) [yes]:

Generated or updated files
Generation path: /var/www/sandbox/web
 1 - /modules/custom/custom_entity_bundle/
 2 - /modules/custom/custom_entity_bundle/custom_entity_bundle.module

So now we should have a custom module containing our info.yml and .module files. One thing to note is I need to manually update the .info.yml generated by Drupal console to ensure the module is Drupal 9 compatible.

Our final .info.yml looks like:

name: 'custom content entity'
type: module
description: 'Custom content entity'
core_version_requirement: ^8 || ^9
package: 'Custom'

Now we have a module skeleton, we can generate our entity, just like generating the module skeleton, we can use Drupal console to do much of the heavy lifting for us.

The command I ran was:

drupal generate:entity:content

Or the alias:

drupal geco

First of all you need to specify the name of the custom module that you created earlier.

$ drupal geco

// Welcome to the Drupal Content Entity generator
Enter the module name [ctools]:
> custom_content_entity

Now you need to specify the name for your custom entity, you can either enter your own on go for the defaults suggested.

Enter the class of your new content entity [DefaultEntity]:
> CustomContentEntity

Enter the machine name of your new content entity [custom_content_entity]:

Enter the label of your new content entity [Custom content entity]:

You should then decide where you want the menu links to live, so that you can access the settings and content listing pages for the custom entity, although for the latter, you can also use the core views module to make pages that meet your sites needs.

Enter the base-path for the content entity routes [/admin/structure]:

Now you need to decide if you want your custom entity to have bundle support, bundles allow you to create multiple types of the same entity within the Drupal UI, instead of having to create a custom entity each time. A core example of this would Node content types or Taxonomy Vocabularies. Also by using bundles, you can try and keep the logic and interfaces contained and hopefully not too confusing.

Do you want this (content) entity to have bundles? (yes/no) [no]:
> yes

Now you need to decide if your custom entity should be setup so that the content can be translatable for multilingual websites. Plus you also need to decide if the content should be revisionable, so that you have the ability to revert content back to earlier version if the need arises. The answers to these two questions depends on you requirements for the website/ entity.

Is your entity translatable? (yes/no) [yes]:

Is your entity revisionable? (yes/no) [yes]:

For the next three questions, I would just go for the default answer of yes, as for most cases, it would be the most logical plan to have these options enabled, the per bundle permissions is useful if you want to have more granular permissions, but this does depend on your individual requirements of course.

Do you want this (content) entity to have forms? (yes/no) [yes]:

Do you want this (content) entity to have an owner? (yes/no) [yes]:

Do you want this entity to have "per bundle" permissions? (yes/no) [yes]:

Now you should see a list of all of the files that have been created as a result of answering the questions above, your list may look very similar to the below list of files.

Generated or updated files
 Generation path: /var/www/sandbox/web
 1 - modules/custom/custom_content_entity/custom_content_entity.permissions.yml
 2 - modules/custom/custom_content_entity/src/CustomContentEntityAccessControlHandler.php
 3 - modules/custom/custom_content_entity/src/CustomContentEntityTranslationHandler.php
 4 - modules/custom/custom_content_entity/src/Entity/CustomContentEntityInterface.php
 5 - modules/custom/custom_content_entity/src/Entity/CustomContentEntity.php
 6 - modules/custom/custom_content_entity/src/Entity/CustomContentEntityViewsData.php
 7 - modules/custom/custom_content_entity/src/CustomContentEntityListBuilder.php
 8 - modules/custom/custom_content_entity/src/CustomContentEntityHtmlRouteProvider.php
 9 - modules/custom/custom_content_entity/
 10 - modules/custom/custom_content_entity/custom_content_entity.links.task.yml
 11 - modules/custom/custom_content_entity/custom_content_entity.links.action.yml
 12 - modules/custom/custom_content_entity/src/Form/CustomContentEntitySettingsForm.php
 13 - modules/custom/custom_content_entity/src/Form/CustomContentEntityForm.php
 14 - modules/custom/custom_content_entity/src/Form/CustomContentEntityDeleteForm.php
 15 - modules/custom/custom_content_entity/templates/custom_content_entity.html.twig
 16 - modules/custom/custom_content_entity/
 17 - modules/custom/custom_content_entity/src/Form/CustomContentEntityRevisionRevertTranslationForm.php
 18 - modules/custom/custom_content_entity/src/Form/CustomContentEntityRevisionDeleteForm.php
 19 - modules/custom/custom_content_entity/src/Form/CustomContentEntityRevisionRevertForm.php
 20 - modules/custom/custom_content_entity/src/Controller/CustomContentEntityController.php
 21 - modules/custom/custom_content_entity/src/CustomContentEntityStorage.php
 22 - modules/custom/custom_content_entity/src/CustomContentEntityStorageInterface.php
 23 - modules/custom/custom_content_entity/templates//custom-content-entity-content-add-list.html.twig
 24 - modules/custom/custom_content_entity/src/CustomContentEntityPermissions.php
 25 - modules/custom/custom_content_entity/custom_content_entity.module
 26 - modules/custom/custom_content_entity/custom_content_entity.module
 27 - modules/custom/custom_content_entity/config/schema/custom_content_entity_type.schema.yml
 28 - modules/custom/custom_content_entity/
 29 - modules/custom/custom_content_entity/custom_content_entity.links.action.yml
 30 - modules/custom/custom_content_entity/src/Entity/CustomContentEntityTypeInterface.php
 31 - modules/custom/custom_content_entity/src/Entity/CustomContentEntityType.php
 32 - modules/custom/custom_content_entity/src/CustomContentEntityTypeHtmlRouteProvider.php
 33 - modules/custom/custom_content_entity/src/Form/CustomContentEntityTypeForm.php
 34 - modules/custom/custom_content_entity/src/Form/CustomContentEntityTypeDeleteForm.php
 35 - modules/custom/custom_content_entity/src/CustomContentEntityTypeListBuilder.php

 Generated lines: 2106

The problems with using Drupal console


At the time of writing, Drupal console isn't setup to support Drupal 9, however it could well be that we should be switching back to using Drush see:

Below are the problems that you will encounter if you are following along and using the Drupal console commands mentioned in this blog post.

You need to update the [module name].info.yml to include the below line:

core_version_requirement: ^8 || ^9


When you come to add a bundle, everything seems to go fine until you try and click save after giving your content entity a name.

Custom entity type listing page

Custom entity type bundle initial add page

In the log, you may see an error like;

Drupal\Core\Config\Schema\SchemaIncompleteException: Entity type 'Drupal\Core\Config\Entity\ConfigEntityType' is missing 'config_export' definition in its annotation in Drupal\Core\Config\Entity\ConfigEntityBase->toArray() (line 251 of /var/www/sandbox/web/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php).

This is solvable by adding the below code to the annotation between entity_keys and links in the entity file that has been generated:

 *   config_export = {
 *     "id",
 *     "label",
 *     "description",
 *     "weight"
 *   },

Look for the file in /path-to-custom-module/custom-module-name/src/Entity.

When we generated the entity, we called ours Custom Content Entity, so we look for a file call CustomContentEntity.php in the Entity folder.

If you have enabled bundles on your content entity, then you will also need to add the missing annotation bit to the file ending in Type.php, so ours is called CustomContentEntityType.php inside our Entity folder.


After enabling the custom module and visiting the Structure tab in the admin backend of the website, you should see a custom extra links, like what is shown in the screenshot below, the link ending in entity type is where you can manage the bundles for the entity type. The link ending in entity list is the default listing page and add screen for the entity type.

Your screens may look a bit like the below screenshots but obviously you may have other bundles and fields etc depending on your requirements.

Structure page showing links
Page showing the content entity bundles listing
Content entity bundle add screen
Manage fields screen for the custom config entity bundle
Custom content entity bundle listing page
Content entity custom bundle add page



This was just a quick blog post to go over creating a custom content entity easily using Drupal console, so we didn't go deep into the files that were created, why they are needed etc.

As mentioned, Drupal console may not become Drupal 9 compatible as it could be that Drush will be use more instead and it looks like there are drush add on commands that can be used to generate the code for entities etc, but this is something that I am yet to explore.


Related blog posts

Creating custom tokens in Drupal 8/9

Authored by Alan Saunders on Sunday, January 17, 2021 - 10:00
Reading Time: 8 minutes

Creating a custom theme in Drupal 8/9

Authored by Alan Saunders on Tuesday, April 13, 2021 - 23:00
Reading Time: 7 minutes

Queues in Drupal 8 and 9

Authored by Alan Saunders on Sunday, December 26, 2021 - 22:00
Reading Time: 5 minutes