Creating custom content entities in Drupal 8/9
Published by Alan Saunders on Wednesday, March 24, 2021 - 22:00
Reading Time: 7 minutes
TitleIntroduction
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: https://drupalconsole.com/.
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/custom_entity_bundle.info.yml
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/custom_content_entity.links.menu.yml
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/custom_content_entity.page.inc
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/custom_content_entity.links.menu.yml
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
TitleThe 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: https://twitter.com/wizonesolutions/status/1258031888553426951
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.
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.
TitleConclusion
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
End to end testing with Cypress
Authored by Alan Saunders on Wednesday, December 28, 2022 - 20:00
Reading Time: 6 minutes
Creating a custom form in Drupal 8/9
Authored by Alan Saunders on Sunday, January 10, 2021 - 10:00
Reading Time: 7 minutes
Creating custom tokens in Drupal 8/9
Authored by Alan Saunders on Sunday, January 17, 2021 - 10:00
Reading Time: 8 minutes