Creating a content entity type in Drupal 8

Based on requirements of a couple projects that I am currently working on, I was in the need to create a content entity type in Drupal 8, and as usual I decided to go ahead and do what every developer does, RTFM and follow the steps as shown on the official documentation.

I was lucky enough to found a link related to my task “Creating a content entity type in Drupal 8” on the official documentation but since code on a project at Alpha state changes a lot the documentation was not up to date, it means the official documentation is the code and as you can guess I faced some challenges that I will list below and the approach I used to overcome it.

While creating this blog post the documentation was update so you can see the original documentation I was reading when started writing this blog post https://www.drupal.org/node/2192175/revisions/7222609/view the only change so far is the migration from PSR-0 to PSR-4 as you can see it here https://www.drupal.org/node/2192175/revisions/7382233/view

Copy source code from documentation

First task was to copy all the source code from the documentation and this was the result


foo_bar/
├── README.md
├── foo_bar.info.yml
├── foo_bar.install
├── foo_bar.local_actions.yml
├── foo_bar.local_tasks.yml
├── foo_bar.module
├── foo_bar.routing.yml
└── lib
    └── Drupal
        └── foo_bar
            ├── Entity
            │   ├── Controller
            │   │   └── FooBarListBuilder.php
            │   ├── FooBar.php
            │   └── Form
            │       ├── FooBarDeleteForm.php
            │       ├── FooBarFormController.php
            │       └── FooBarSettingsForm.php
            └── FooBarInterface.php

6 directories, 13 files

Source code at github https://github.com/jmolivas/foo_bar/tree/1-source-code-from-documentation

Update to PSR-4

As you can see the directory structure is based on PSR-0 but Drupal modules must be PSR-4 so I updated with the following result.


foo_bar/
├── README.md
├── foo_bar.info.yml
├── foo_bar.install
├── foo_bar.local_actions.yml
├── foo_bar.local_tasks.yml
├── foo_bar.module
├── foo_bar.routing.yml
└── src
    ├── Entity
    │   ├── Controller
    │   │   └── FooBarListBuilder.php
    │   ├── FooBar.php
    │   └── Form
    │       ├── FooBarDeleteForm.php
    │       ├── FooBarFormController.php
    │       └── FooBarSettingsForm.php
    └── FooBarInterface.php

4 directories, 13 files

Source code at github https://github.com/jmolivas/foo_bar/tree/2-update-to-psr-4 

The "foo_bar" entity type did not specify a list_builder class

After installing the module and trying to visit the route "foo-bar/list" the following error was triggered.


Uncaught PHP Exception Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: 
"The "foo_bar" entity type did not specify a list_builder class." at 
/core/lib/Drupal/Core/Entity/EntityManager.php line 293

Fix: Replace on "src/Entity/FooBar.php" class at the plugin controllers declaration "list" with "list_builder" and since "FooBarListController" class does not exist, point it to the correct class "FooBarListBuilder"


- "list" = "Drupal\foo_bar\Entity\Controller\FooBarListController",
+ "list_builder" = "Drupal\foo_bar\Entity\Controller\FooBarListBuilder",

After this changes the route "foo-bar/list" was properly working

Source code at github https://github.com/jmolivas/foo_bar/tree/3-list-builder

ContentEntityFormController class not found

When trying to add new content using the follwing route "foo-bar/add" the following error was triggered


PHP Fatal error:  Class 'Drupal\Core\Entity\ContentEntityFormController' 
not found in /modules/foo_bar/src/Entity/Form/FooBarFormController.php 
on line 12

Fix: "ContentEntityFormController" class not longer exist it was repalced with "ContentEntityForm" then the next step was to replace it on "FooBarFormController" class


-use Drupal\Core\Entity\ContentEntityFormController;
+use Drupal\Core\Entity\ContentEntityForm;

-class FooBarFormController extends ContentEntityFormController {
+class FooBarFormController extends ContentEntityForm {

After this changes the route "foo-bar/add" was properly working

Source code at github https://github.com/jmolivas/foo_bar/tree/4-ContentEntityFormController-class-not-found

Integrity constraint violation: 1048 Column "type" cannot be null

When trying to save the values in the form and click save the following error was triggered


Uncaught PHP Exception Drupal\Core\Entity\EntityStorageException: 
"SQLSTATE[23000]: Integrity constraint violation: 
1048 Column 'type' cannot be null

Fix: Set "type" form field default value as Entity Type Id at "form" method on "FooBarFormController" class


+ $form['type'] = array(
+   '#type' => 'hidden',
+   '#default_value' => $entity->getEntityTypeId(),
+ );

Source code at github https://github.com/jmolivas/foo_bar/tree/5-integrity-constraint-violation

Call to a member function toRenderArray() on a non-object

When trying to delete any content using "foo-bar/{id}/delete" the following error was triggered


PHP Fatal error:  Call to a member function toRenderArray() 
on a non-object in /core/lib/Drupal/Core/Form/ConfirmFormHelper.php 
on line 44

Fix: Return an URL object on getCancelRoute method at "FooBarDeleteForm" Class


+use Drupal\Core\Url;

- return array(
-   'route_name' => 'foo_bar.list',
- );
+ return new Url('foo_bar.list');

After this changes the route "foo-bar/{id}/delete" was properly working and I was able to delete content

Source code at github https://github.com/jmolivas/foo_bar/tree/6-call-to-member-function-on-non-object

Update the documentation

Finally after the code was properly working you can get the latest version at this github repository https://github.com/jmolivas/foo_bar

I decide to update the documentation with the generated code, you can see the updated version here https://www.drupal.org/node/2192175/revisions/7382931/view

Feel free to send any corrections, improvements or comments related to the new code.