Series Parts:
- Part 1: Overview of Content and Config Entities in Drupal 8
- Part 2: Most Simple Entity with Bundles
- Part 3: Simple Content Entity with Bundles
- Part 4: Practical Content Entity with Bundles
Disclaimer: This article assumes you have a working knowledge of the following things:
- Drupal’s Administration UI
- PHP OOP syntax. Understanding namespaces will be helpful, but not 100% necessary to get the main points.
- YAML syntax.
All of the examples used in this post are available in my drupal8_examples Github repo
This post covers:
- Content Entities & Bundle Config Entities
- Annotations
- Entity Base Fields
- Entity Forms
- Routing & Menu items
- List Builders
- Generating Permissions
- Access Control
About Entity API
Entities in Drupal 7+ are generic data models that are extended for specific functionality. For example, in Drupal 8 all of the following things are based on the generic Entity: Nodes, Comments, Users, and Blocks. Being entities means that each item shares the same foundation that offer common functionality, such as CRUD (create/read/update/delete) operations. A more Drupal-like example of shared functionality would be “fieldable”. Each of the items listed above can have custom fields added to them because they are all extensions of the foundational Drupal Entity.
The Entity API is how developers create, extend, and otherwise interact with entities in Drupal.
This post is an introduction to creating custom entities and bundles of those entities in Drupal 8. Let’s get started! The first thing you need to know is that Entities come in 2 main flavors. The Content Entity and the Configuration Entity.
Content Entity
Content Entities are stored in their own database table and are normally displayed on the site in some manner. All of the examples listed above are Content Entities, such as Nodes, Blocks, etc. That is not to say all Content Entities must be displayed, just that it is common to do so.
The most familiar example of a Content Entity is the Node, which are stored as rows within the node
table in the database
Configuration Entity
Configuration Entities are based on the Config API, but used to store multiple sets of configuration data. Config Entity stores its data in the config
database table, prefixed by the module name. A module can define additional prefixes for a configuration entity in its definition (annotation). This will be made more clear in the examples.
Simple description of Configuration data in general: Information about your site that is not content and changes infrequently, such as the name of your site.
Bundles
Bundles are a special variant of the Configuration Entity. They are Configuration Entities that enhance a content entity by providing mechanisms for having different Content Entity “Types”. A bundle configuration entity stores the different configurations for the various Content Entities, such as settings and custom fields.
The most notable example of Bundles in Drupal are Node Types (aka, Content Types). In the core Node module “Content Types” are setup as a configuration entity that stores the various fields and settings for an individual content type. While Nodes themselves are Content Entities that store the values for a single node.
Confused yet?
The mixture of terminology between “Bundle” vs “Type” are due to legacy Drupal versions. In Drupal 6 and lower, variants of a node were once called a “Node Type” and later renamed to “Content Type” for UX purposes. Drupal 7 introduced the concept of “Entities” and “Entity Types”, as well as the terminology “Bundle” to mean a sub-type of an Entity Type.
A simple breakdown of how this looks hierarchically:
- Entity Type: Content Entity
- Content Entity Type: Node
- Bundle: Article
- Bundle: Basic Page
- Content Entity Type: User
- Has no bundles
- Content Entity Type: Node
For an overview of different types of data within Drupal, see this documentation page.
Annotations
The Drupal 8 Entity API leverages the concept of Annotations to a great degree. Annotations look like common PHP docblock comments, but are parsed by the system to provide basic information/settings to the PHP class or method the annotations represent. Annotations in Drupal 8 are a product of using Symfony as the underlining framework. Drupal has extended the Symfony annotations system with its own custom annotations.
Annotation Syntax
@Function()
- Function (or, “Object”) to execute with the contained parameters.
parameter = "value"
- Parameter (or, “property”) that is passed into a function. Note that the parameter name itself is not surrounded by quotation marks.
array_parameter = { "some_key" = "some_value" }
- Parameter that represents an array. Note the use of curly braces to define the array, and how keys in the array are surrounded by quotation marks.
- Docblock
- Annotations are contained within the syntax of a docblock, and appear immediately before a class or method.
- Commas
- Commas must separate parameters as well as items in an array.
Syntax usage example:
/** * @Function( * parameter = "value", * array_parameter = { * "some_key" = "some_value", * "another_key" = "another_value" * } * ) */
When viewed this way, it should become apparent where commas are necessary. You may also include commas after the final parameter or item in an array. I do this because it’s easier for me to remain consistent.
Entity Annotation Examples
Content Example:
Okay, that’s a lot of moving parts. Let’s break it down a little bit.
New Annotations
@ContentEntityType()
- Object that is instantiated with the contained parameters.
id
- Machine-safe name for the Entity.
label
- Human readable name for the Entity, passed through the
@Translation()
function so it can be translated by the UI. base_table
- Database table where this Entity is stored. This should be a custom table name, used only by your new Entity.
entity_keys
- Array of aliases for base fields on the Entity. In this example, none of the field names are aliased. More about this later.
fieldable
- Boolean value for whether or not this Entity can have Drupal fields attached/assigned to it through the Fields UI.
admin_permission
- Permission used to allow adminstrative access to this entity’s settings / configuration.
handlers
- Array map of common Entity operations to PHP classes that handle the functionality.
form
- Array map of common Entity form names to PHP classes that handle the form operations.
route_provider
- Array map of of route types to PHP classes. I’ve only personally dealt with the “html” route provider.
links
- Array map of common Entity route names to the URIs where the functionality can be found.
bundle_entity_type
- Id of the Config Entity that serves as bundles for this Content Entity.
field_ui_base_route
- The name of the Entity route where the Fields UI should be attached.
Note:
It is not required to explicitly specify all of these annotation properties. Many of the ones shown here (especially “handlers”) are automatically generated when not provided. In my examples, I specify the default values of these properties in order to better understand what is expected and what happening behind the scenes.
Bundle Config Example:
New Annotations
@ConfigEntityType()
- Object that is instantiated with the provided properties.
bundle_of
- Id for the Content Entity that this Config Entity enhances as a bundle.
config_prefix
- Additional prefix for the values saved to the
config
database table. config_export
- Base fields that will be exported along with the entity.
form
- Though not completely new, it’s worth pointing out that a custom Config Entity needs to provide its own Form handler for “default”, “add”, and “edit”. More on this later.
Note:
In many cases, it’s fair to consider annotations as a replacement for an older Drupal 7- hook. Looking at the examples, you can imagine how in previous versions of Drupal you might be required to leverage a hook within your custom module that returns an array with these settings.
Enough with the no-context examples, let’s make it work!
Next – Part 2: Most Simple Entity with Bundles
References
Resources used to put all this together
- Drupal Console –
drupal generate:entity:content
command - Drupal 8 Developer’s Cookbook – by Matt Glaman
- Profile – Module by Commerce Guys
- Group – Module by Kristiaan Van den Eynde
- …and a whole lot of Google.
See anything in this post that is incorrect or you think could be better explained? Let me know below!
Discussion
Hi Jonathan Daggerhart
I have did follow your article. But file routing.yml error at entity.simple.add_page. And When I goto /simple/add/{simple_type}. It have error “Fatal error: Call to a member function id() on string”
Can you share for me your entities?
Hi Peter,
Yes, all of these examples are on Github. Here is a link to the “simple” entity module – https://github.com/daggerhart/custom_entities/tree/master/modules/simple
Let me know if you run into other problems.
A Great article! Finally, I have found additional information about the annotation items.
P.S. There few places you misspelled the word should (“shodld”)
Thanks Nikolay, I’m glad these articles are helpful. And thanks for the spell-check! How did I manage to misspell it twice that way? ?
Very informative article. :)
Thank you, very helpful article.