Drupal 9: Creating A Minimal Content Entity

Drupal 9: Creating A Minimal Content Entity

21st August 2022 - 8 minutes read time

I have recently been looking at generating custom content entities and this lead to generating a minimal entity that would be useful on a Drupal site.

If you've ever used Drupal Console to generate a content entity, then you'll know what it generates a lot of files. There's all sorts of classes and configuration files generated that handle everything from generating lists of entities and forms for creating new entities.

This got me thinking about what is the minimal amount of configuration needed to generate a usable content entity. This might be used to store some simple data or to attach to other entities through an entity reference. As it happens, setting up a minimal content entity takes just a single file.

First, we need to set up an interface so that we can identify the content entity within the system. The entity is called MinimalContentEntityInterface and is located in the file src/Entity/MinimalContentEntityInterface.php within a custom module (called mymodule in this case).

<?php

namespace Drupal\mymodule\Entity;

use Drupal\Core\Entity\ContentEntityInterface;

interface MinimalContentEntityInterface extends ContentEntityInterface {

}

This interface extends the ContentEntityInterface interface, which is the common interface used by all content entities in Drupal.

Next, we need to add the entity class. This is called MinimalContentEntity and is located in the file at src/Entity/MinimalContentEntity.php in the same custom module.

What we need to do in this class is add an annotation that informs Drupal of the presence of the entity. This sets the table name used to store the entity data and the entity keys we need.

We also need to create a static method called baseFieldDefinitions(), which returns the field definitions we require to create the entity.

<?php

namespace Drupal\mymodule\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;

/**
 * Class MinimalContentEntity.
 *
 * @package Drupal\mymodule\Entity
 *
 * @ContentEntityType(
 *   id = "minimal_content_entity",
 *   label = @Translation("Minimal Content Entity"),
 *   base_table = "minimal_content_entity",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label"
 *   }
 * )
 */
class MinimalContentEntity extends ContentEntityBase implements MinimalContentEntityInterface {

  /**
   * {@inheritDoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = [];

    $fields[$entity_type->getKey('id')] = BaseFieldDefinition::create('integer')
      ->setLabel(new TranslatableMarkup('ID'))
      ->setReadOnly(TRUE)
      ->setSetting('unsigned', TRUE);

    $fields['label'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Label'))
      ->setDescription(t('The label of the entity.'));

    return $fields;
  }
}

This new entity class extends the ContentEntityBase class and also implements the MinimalContentEntityInterface interface. This means that we get a lot of useful code for free that we can use on the entity. 

The minimal_content_entity contains just two fields. The ID field is used to identify the entity on the system and the label is used to give it a readable name. We could just create an entity with just the ID field, but the label allows for things like entity reference fields to work correctly with this minimal entity.

When we install this module a table called minimal_content_entity is added to the Drupal database. This will be the storage location of the entity in our system.

To create the entity we just need to generate it like any other entity, using the entity_type.manager service. In this case we don't need to add the ID field as the field is setup as an auto-increment field and so is generated automatically.

$data = [
  'label' => 'Some title',
];
$newEntity = \Drupal::entityTypeManager()
  ->getStorage('minimal_content_entity')
  ->create($data);
$newEntity->save();

This adds the content entity to the table minimal_content_entity. If we do a SQL query on that table we can see the data in there.

> select * from minimal_content_entity;
+----+------------+
| id | label      |
+----+------------+
|  1 | Some title |
+----+------------+

We can load and use this entity just like any entity in Drupal through the same entity_type.manager service.

$minimalContentEntity = \Drupal::entityTypeManager()
  ->getStorage('minimal_content_entity')
  ->load(1);

With this entity object loaded we can use it just like a normal entity. We can make use of the typed data interface to grab the data from the fields or use the built in helper methods from the ContentEntityBase class to load the fields we need.

// Get the ID.
$id = $minimalContentEntity->id();

// Get the label via the typed data interface.
$label = $minimalContentEntity->get('label')->value

// Get the label through the entity class.
$label = $minimalContentEntity->label();

The label field is one of the default fields we can use in custom entities that gets a handy method like label(). Other fields that allow this are revision, bundle, langcode, and uuid, each of which has its on use within the content entity.

The minimal_content_entity we have created here is a useful example for learning about content entities, but also has its uses within a Drupal site. You can use it as a kind of simple taxonomy term that you can tag other entities with. The entity reference field uses the Label attribute to search for entities, and the entity itself can be extended to store any kind of data you need.

As an example, let's say that you were pulling some data from an API that you needed to store against a user. You could add the data fields you need to store to a simple entity like this and then simply associate the entity to the user. This allows you to handle the API data within the Drupal site and you would only need to handle the API and entity creation code to get this working.

There's a lot more to learn about content entities. In fact the annotation allows for a variety of different options and attributes to be set. You can set custom handling forms or list builders, links to the entities on the site, and even administration permissions. There's plenty of information on the Drupal website about creating the various parts required for a content entity that will take this minimal entity and make it much more useful.

Comments

Permalink

Thanks Andi. There's always a module out there to do whatever you need do to ;)

Permalink

I don't think you need the interface at all, especially for custom code rather than contrib; your entity class can just extend ContentEntityInterface.

Jonathan Shaw (Mon, 08/22/2022 - 18:06)

Permalink

Thanks for reading Jonathan, the reason the interface is there is for good SOLID design principles. If we ever need to pass the entity to function we can use the interface to ensure that we are following the dependency inversion principle.

Permalink

This is a great idea. I would have also said, skip the interface and only introduce it when needed. Meanwhile, your entity class could implement ContentEntityInterface directly.

Have you considered contributing that to https://github.com/chi-teck/drupal-code-generator as it would then be available for everyone using Drush out of the box.

Jürgen Haas (Thu, 08/25/2022 - 15:55)

Add new comment

The content of this field is kept private and will not be shown publicly.