Bundles allow you to have different variations of a content entity. All bundles share the same base field definitions but not configured fields. This allows each bundle to have its own custom fields. Display modes are also dependent on a specific bundle. This allows each bundle to have its own configuration for the form mode and view mode.
Using the custom entity from the previous recipe, we will add a configuration entity to act as the bundle. This will allow you to have different message types for multiple custom field configurations.
You will need a custom module to place the code into in order to implement a configuration entity type. Create an src
directory for your classes. We need a custom content entity type to be implemented, such as the one in the Creating a content entity type recipe.
config/schema
directory and a mymodule.schema.yml
file that will contain the configuration entity's schema:mymodule.message_type.*: type: config_entity label: 'Message type settings' mapping: id: type: string label: 'Machine-readable name' uuid: type: string label: 'UUID' label: type: label label: 'Label' langcode: type: string label: 'Default language'
message_type
, which we will provide to Drupal in the entity's annotation block. We tell Drupal that this is a config_entity
and provide a label for the schema.src/Entity
directory, create an interface for our bundle by making a Me
ssageTypeInterface.php
file. The MessageTypeInterface
will extend the DrupalCoreConfigEntityConfigEntityInterface
:<?php /** * @file Contains DrupalmymoduleEntityMessageTypeInterface. */ namespace DrupalmymoduleEntity; use DrupalCoreConfigEntityConfigEntityInterface; interface MessageTypeInterface extends ConfigEntityInterface { // Empty for future enhancements. }
MessageType.php
file in src/Entity
. This will hold the MessageType
class, which will extend DrupalCoreConfigEntityConfigEntityBundleBase
and implement our bundle's interface:<?php /** * @file Contains DrupalmymoduleEntityMessageType. */ namespace DrupalmymoduleEntity; use DrupalCoreConfigEntityConfigEntityBundleBase; class MessageType extends ConfigEntityBundleBase implements MessageTypeInterface { }
ID
, label
, entity
keys, and configuration export
keys:<?php /** * @file Contains DrupalmymoduleEntityMessageType. */ namespace DrupalmymoduleEntity; use DrupalCoreConfigEntityConfigEntityBundleBase; /** * Defines the profile type entity class. * * @ConfigEntityType( * id = "message_type", * label = @Translation("Message type"), * config_prefix = "message_type", * bundle_of = "message", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid", * "langcode" = "langcode" * }, * config_export = { * "id", * "label", * } * ) */ class MessageType extends ConfigEntityBundleBase implements MessageTypeInterface { }
ConfigEntityType
plugin. The id
is the internal machine name identifier for the entity type and the label
is the human-readable version. The config_prefix
matches with how we defined our schema with mymodule.message_type
. The entity keys definition tells Drupal which attributes represent our identifiers and labels.config_export
, we are telling the configuration management system what properties are to be exported when exporting our entity./** * Defines the profile type entity class. * * @ConfigEntityType( * id = "message_type", * label = @Translation("Message type"), * handlers = { * "list_builder" = "DrupalmymoduleMessageTypeListBuilder", * "form" = { * "default" = "DrupalCoreEntityEntityForm", * "add" = "DrupalCoreEntityEntityForm", * "edit" = "DrupalCoreEntityEntityForm", * "delete" = "DrupalCoreEntityEntityDeleteForm" * }, * }, * config_prefix = "message_type", * bundle_of = "message", * entity_keys = { * "id" = "id", * "label" = "label", * "uuid" = "uuid", * "langcode" = "langcode" * }, * config_export = { * "id", * "label", * }, * ) */
handlers
array specifies classes that provide the interaction functionality with our entity. The list builder class will be created to show you a table of our entities. The form array provides classes for forms to be used when creating, editing, or deleting our configuration entity.route_provider
, to dynamically generate our canonical (view), edit, and delete routes:/** * Defines the profile type entity class. * * @ConfigEntityType( * id = "message_type", * label = @Translation("Message type"), * handlers = { * "list_builder" = "DrupalprofileMessageTypeListBuilder", * "form" = { * "default" = "DrupalCoreEntityEntityForm", * "add" = "DrupalCoreEntityEntityForm", * "edit" = "DrupalCoreEntityEntityForm", * "delete" = "DrupalCoreEntityEntityDeleteForm" * }, * "route_provider" = { * "html" = "DrupalCoreEntityRoutingDefaultHtmlRouteProvider", * }, * }, * config_prefix = "message_type", * bundle_of = "message", * entity_keys = { * "id" = "id", * "label" = "label" * }, * config_export = { * "id", * "label", * }, * links = { * "delete-form" = "/admin/structure/message-types/{message_type}/delete", * "edit-form" = "/admin/structure/message-types/{message_type}", * "admin-form" = "/admin/structure/message-types/{message_type}", * "collection" = "/admi n/structure/message-types" * } * ) */
/** * Defines the profile entity class. * * @ContentEntityType( * id = "message", * label = @Translation("Message"), * handlers = {...}, * base_table = "message", * fieldable = TRUE, * bundle_entity_type = "message_type", * field_ui_base_route = "entity.message_type.edit_form", * entity_keys = { * "id" = "message_id", * "label" = "title", * "langcode" = "langcode", * "bundle" = "type", * "uuid" = "uuid" * }, * links = {...}, * ) */
bundle_entity_type
key specifies the entity type used as the bundle. The plugin validates this as an actual entity type and marks it for configuration dependencies. With the field_ui_base_route
key pointed to the bundle's main edit form, it will generate the Manage Fields
, Manage Form Display
, and Manage Display
tabs on the bundles. Finally, the bundle
entity key instructs Drupal which field definition to use in order to identify the entity's bundle, which is created in the next step.type
field that we defined to represent the bundle
entity key:$fields['type'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Message type')) ->setDescription(t('The message type.')) ->setSetting('target_type', 'message_type') ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
mymodule.routing.yml
, provide a route for adding a Message Type
entity:entity.message_type.add_form: path: '/admin/structure/message-types/add' defaults: _entity_form: 'message_type.add' _title: 'Add message type' requirements: _entity_create_access: 'message_type'
_entity_form
property to tell Drupal to look up the class defined in our handlers.list_builder
handler, we also need to add the route to routing.yml
for our collection link definition, as this is not auto generated by route providers:entity.message_type.collection: path: '/admin/structure/message-types' defaults: _entity_list: 'message_type' _title: 'Message types' requirements: _permission: 'administer message types'
_entity_list
key will tell the route to use our list_builder
handler to build the page.MessageTypeListBuilder
class defined in our list_builder
handler in a MessageTypeListBuilder.php
file and extend DrupalCoreConfigEntityConfigEntityListBuilder
. We need to override the default implementation to display our configuration entity properties:<?php /** * @file Contains DrupalmymoduleMessageListBuilder */ namespace Drupalmymodule; use DrupalCoreEntityEntityInterface; use DrupalCoreConfigEntityConfigEntityListBuilder; class MessageTypeListBuilder extends EntityListBuilder { public function buildHeader() { $header['label'] = t('Label'); return $header + parent::buildHeader(); } public function buildRow(EntityInterface $entity) { $row['label'] = $entity->label(); return $row + parent::buildRow($entity); } }
buildHeader
and builderRow
methods so that we can add our configuration entity's properties to the table:Bundles are most utilized in the configured field levels via the Field
and Field UI
modules. When you create a new field, it has a base storage item for its global settings. Once a field is added to a bundle, there is a new field configuration that is created and assigned to the bundle. Fields can then have their own settings for a specific bundle along with form and view display configurations.
Content entity bundles work just like any other configuration entity implementation, but they extend the usability of the Field API for your content entity types.
We will discuss how to add additional functionality to your entity bundle, and use the Entity module to simplify the developer expedience.
There are special links called action links in Drupal. These appear at the top of the page and are generally used for links that allow the creation of an item by creating a links.action.yml
file.
In your mymodule.links.action.yml
, each action link defines the route it will link to, titles, and the routes it appears on:
message_type_add: route_name: entity.message_type.add_form title: 'Add message type' appears_on: - entity.message_type.collection
The appears_on
key accepts multiple values that will allow this route link to appear on multiple pages: