Creating a bundle for a content entity type

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.

Getting ready

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.

How to do it…

  1. Since content entity bundles are configuration entities, we need to define our configuration entity schema. Create a 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'
  2. We define the configuration entity's config prefix as 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.
  3. With the mapping array, we provide the attributes that make up our entity and the data that will be stored.
  4. In your module's src/Entity directory, create an interface for our bundle by making a MessageTypeInterface.php file. The MessageTypeInterface will extend the DrupalCoreConfigEntityConfigEntityInterface:
    <?php
    
    /**
     * @file Contains DrupalmymoduleEntityMessageTypeInterface.
     */
    
    namespace DrupalmymoduleEntity;
    
    use DrupalCoreConfigEntityConfigEntityInterface;
    
    interface MessageTypeInterface extends ConfigEntityInterface {
      // Empty for future enhancements.
    }
  5. This will be implemented by our entity and provide the method requirements. It is best practice to provide an interface for entities. This allows you to provide required methods if another developer extends your entity or if you are doing advanced testing and need to mock an object.
  6. We will be implementing a very basic bundle. It is still wise to provide an interface in the event of future enhancements and mocking ability in tests.
  7. Create a 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 {
    
    }
  8. In most use cases, the bundle entity class can be an empty class that does not provide any properties or methods. If a bundle provides additional attributes in its schema definition, they would also be provided here, like any other configuration entity.
  9. Entities need to be annotated. Create a base annotation for the 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 {
    
    }
  10. The annotation document block tells Drupal that this is an instance of the 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.
  11. When specifying config_export, we are telling the configuration management system what properties are to be exported when exporting our entity.
  12. We will then add handlers, which will interact with 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",
     *   },
     * )
     */
  13. The 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.
  14. An additional handler can be added, the 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"
     *   }
     * )
     */
  15. There is a routing service for entities that will automatically provide Drupal a route with the proper controllers based on this annotation. The add form route is not yet supported and needs to be manually added.
  16. We need to modify our content entity to use the bundle configuration entity that we defined:
    /**
     * 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 = {...},
     * )
     */
  17. The 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.
  18. A new field definition needs to be added to provide the 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);
  19. The field that identifies the bundle will be typed as an entity reference. This allows the value to act as a foreign key to the bundle's base table.
  20. In your 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'
  21. We can use the _entity_form property to tell Drupal to look up the class defined in our handlers.
  22. Before we implement our 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'
  23. The _entity_list key will tell the route to use our list_builder handler to build the page.
  24. Create the 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);
      }
    
    }
  25. In our list builder handler, we override the buildHeader and builderRow methods so that we can add our configuration entity's properties to the table:
    How to do it…
  26. Your module's structure should resemble the following screenshot:
    How to do it…

How it works…

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.

There's more…

We will discuss how to add additional functionality to your entity bundle, and use the Entity module to simplify the developer expedience.

Provide action links for adding new bundles

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:

Provide action links for adding new bundles

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset