Subscribe To Our NewsLetter
Share This Post:
Custom Entities are a powerful tool for building complex web applications and content management systems. Entities in Drupal provide a standardized way to store and manipulate data. Custom entity types in Drupal empower developers to define custom functionality, enhance performance, and maintain full control over data structures, supplementing the numerous built-in entity types.
Here are the steps for creating a custom entity.
Drupal 10 Custom Entity Development in easy steps:
Step 1: Create a custom folder for your module.
Choose a short name or machine name for your module.
Let’s name it: content_entity_example
Step 2: Create a info.yml file:
Content_entity_example.info.yml
name: Content Entity Example
type: module
description: 'Provides Custom Entity'
package: Other
core_version_requirement: ^8 || ^9 || ^10
Step 3: Routing file
content_entity_example.routing.yml
# Route name can be used in several places; e.g. links, redirects, and local
# actions.
entity.content_entity_example_contact.canonical:
path: '/content_entity_example_contact/{content_entity_example_contact}'
defaults:
# Calls the view controller, defined in the annotation of the contact entity
_entity_view: 'content_entity_example_contact'
_title: 'Contact Content'
requirements:
# Calls the access controller of the entity, $operation 'view'
_entity_access: 'content_entity_example_contact.view'
entity.content_entity_example_contact.collection:
path: '/content_entity_example_contact/list'
defaults:
# Calls the list controller, defined in the annotation of the contact entity.
_entity_list: 'content_entity_example_contact'
_title: 'Student Registration List'
requirements:
# Checks for permission directly.
_permission: 'administer contact entity'
content_entity_example.contact_add:
path: '/content_entity_example_contact/add'
defaults:
# Calls the form.add controller, defined in the contact entity.
_entity_form: content_entity_example_contact.add
_title: 'Add Student'
requirements:
_entity_create_access: 'content_entity_example_contact'
content_entity_example.contact_export_data:
path: '/content_entity_example_contact/add'
defaults:
# Calls the form.add controller, defined in the contact entity.
_entity_form: content_entity_example_contact.add
_title: 'Add Student'
requirements:
_entity_create_access: 'content_entity_example_contact'
entity.content_entity_example_contact.edit_form:
path: '/content_entity_example_contact/{content_entity_example_contact}/edit'
defaults:
# Calls the form.edit controller, defined in the contact entity.
_entity_form: content_entity_example_contact.edit
_title: 'Edit Contact'
requirements:
_entity_access: 'content_entity_example_contact.edit'
entity.content_entity_example_contact.delete_form:
path: '/contact/{content_entity_example_contact}/delete'
defaults:
# Calls the form.delete controller, defined in the contact entity.
_entity_form: content_entity_example_contact.delete
_title: 'Delete Contact'
requirements:
_entity_access: 'content_entity_example_contact.delete'
content_entity_example.contact_settings:
path: 'admin/structure/content_entity_example_contact_settings'
defaults:
_form: '\Drupal\content_entity_example\Form\ContactSettingsForm'
_title: 'Contact Settings'
requirements:
_permission: 'administer contact entity'
content_entity_example.contact_pdf-download:
path: '/pdf-download'
defaults:
_controller: '\Drupal\content_entity_example\Controller\PdfdownloadForm'
_title: 'Contact Pdf download'
requirements:
_permission: 'administer contact entity'
Step 4: Create content_entity_example.links.menu.yml
# Define the menu links for this module
entity.content_entity_example_contact.collection:
title: 'Content Entity Example: Student Registration Listing'
route_name: entity.content_entity_example_contact.collection
description: 'List Contacts'
parent: system.admin_structure
weight: 10
content_entity_example_contact.admin.structure.settings:
title: Contact Settings
description: 'Configure Contact entity'
route_name: content_entity_example.contact_settings
parent: system.admin_structure
Step 5: Create content_entity_example.links.action.yml
# All action links for this module
content_entity_example.contact_add:
# Which route will be called by the link
route_name: content_entity_example.contact_add
title: 'Add Student'
# Where will the link appear, defined by route name.
appears_on:
- entity.content_entity_example_contact.collection
- entity.content_entity_example_contact.canonical
Step 6: Create Entity type classes
Create a src folder then inside this folder
Create ContactInterface.php
<?php
namespace Drupal\content_entity_example;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\Core\Entity\EntityChangedInterface;/**
* Provides an interface defining a Contact entity.
* @ingroup content_entity_example
*/
interface ContactInterface extends ContentEntityInterface, EntityOwnerInterface, EntityChangedInterface {
}
?>
a. Then create src/Entity/Contact.php
<?php
namespace Drupal\content_entity_example\Entity;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\content_entity_example\ContactInterface;
use Drupal\user\UserInterface;
/**
* Defines the Contact entity.
*
* @ingroup content_entity_example
*
* This is the main definition of the entity type. From it, an entityType is
* derived. The most important properties in this example are listed below.
*
* id: The unique identifier of this entityType. It follows the pattern
* 'moduleName_xyz' to avoid naming conflicts.
*
* label: Human readable name of the entity type.
*
* handlers: Handler classes are used for different tasks. You can use
* standard handlers provided by D8 or build your own, most probably derived
* from the standard class. In detail:
*
* - view_builder: we use the standard controller to view an instance. It is
* called when a route lists an '_entity_view' default for the entityType
* (see routing.yml for details. The view can be manipulated by using the
* standard drupal tools in the settings.
*
* - list_builder: We derive our own list builder class from the
* entityListBuilder to control the presentation.
* If there is a view available for this entity from the views module, it
* overrides the list builder. @todo: any view? naming convention?
*
* - form: We derive our own forms to add functionality like additional fields,
* redirects etc. These forms are called when the routing list an
* '_entity_form' default for the entityType. Depending on the suffix
* (.add/.edit/.delete) in the route, the correct form is called.
*
* - access: Our own accessController where we determine access rights based on
* permissions.
*
* More properties:
* - base_table: Define the name of the table used to store the data. Make sure
* it is unique. The schema is automatically determined from the
* BaseFieldDefinitions below. The table is automatically created during
* installation.
*
* - fieldable: Can additional fields be added to the entity via the GUI?
* Analog to content types.
*
* - entity_keys: How to access the fields. Analog to 'nid' or 'uid'.
*
* - links: Provide links to do standard tasks. The 'edit-form' and
* 'delete-form' links are added to the list built by the
* entityListController. They will show up as action buttons in an additional
* column.
*
* There are many more properties to be used in an entity type definition. For
* a complete overview, please refer to the '\Drupal\Core\Entity\EntityType'
* class definition.
*
* The following construct is the actual definition of the entity type which
* is read and cached. Don't forget to clear cache after changes.
*
* @ContentEntityType(
* id = "content_entity_example_contact",
* label = @Translation("Contact entity"),
* handlers = {
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "list_builder" = "Drupal\content_entity_example\ContactListBuilder",
* "views_data" = "Drupal\views\EntityViewsData",
* "form" = {
* "add" = "Drupal\content_entity_example\Form\ContactForm",
* "edit" = "Drupal\content_entity_example\Form\ContactForm",
* "delete" = "Drupal\content_entity_example\Form\ContactDeleteForm",
* },
* "access" = "Drupal\content_entity_example\ContactAccessControlHandler",
* },
* base_table = "contact",
* admin_permission = "administer contact entity",
* fieldable = TRUE,
* entity_keys = {
* "id" = "id",
* "label" = "name",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/content_entity_example_contact/{content_entity_example_contact}",
* "edit-form" = "/content_entity_example_contact/{content_entity_example_contact}/edit",
* "delete-form" = "/contact/{content_entity_example_contact}/delete",
* "collection" = "/content_entity_example_contact/list"
* },
* field_ui_base_route = "content_entity_example.contact_settings",
* )
*
* The 'links' above are defined by their path. For core to find the corresponding
* route, the route name must follow the correct pattern:
*
* entity.<entity-name>.<link-name> (replace dashes with underscores)
* Example: 'entity.content_entity_example_contact.canonical'
*
* See routing file above for the corresponding implementation
*
* The 'Contact' class defines methods and fields for the contact entity.
*
* Being derived from the ContentEntityBase class, we can override the methods
* we want. In our case we want to provide access to the standard fields about
* creation and changed time stamps.
* Our interface (see ContactInterface) also exposes the EntityOwnerInterface.
* This allows us to provide methods for setting and providing ownership
* information.
*
* The most important part is the definitions of the field properties for this
* entity type. These are of the same type as fields added through the GUI, but
* they can by changed in code. In the definition we can define if the user with
* the rights privileges can influence the presentation (view, edit) of each
* field.
*/
class Contact extends ContentEntityBase implements ContactInterface {
use EntityChangedTrait; // Implements methods defined by EntityChangedInterface.
/**
* {@inheritdoc}
*
* When a new entity instance is added, set the user_id entity reference to
* the current user as the creator of the instance.
*/
public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
parent::preCreate($storage_controller, $values);
$values += array(
'user_id' => \Drupal::currentUser()->id(),
);
}
/**
* {@inheritdoc}
*/
public function getCreatedTime() {
return $this->get('created')->value;
}
/**
* {@inheritdoc}
*/
public function getOwner() {
return $this->get('user_id')->entity;
}
/**
* {@inheritdoc}
*/
public function getOwnerId() {
return $this->get('user_id')->target_id;
}
/**
* {@inheritdoc}
*/
public function setOwnerId($uid) {
$this->set('user_id', $uid);
return $this;
}
/**
* {@inheritdoc}
*/
public function setOwner(UserInterface $account) {
$this->set('user_id', $account->id());
return $this;
}
/**
* {@inheritdoc}
*
* Define the field properties here.
*
* Field name, type and size determine the table structure.
*
* In addition, we can define how the field and its content can be manipulated
* in the GUI. The behaviour of the widgets used can be determined here.
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
// Standard field, used as unique if primary index.
$fields['id'] = BaseFieldDefinition::create('integer')
->setLabel(t('ID'))
->setDescription(t('The ID of the Contact entity.'))
->setReadOnly(TRUE);
// Standard field, unique outside of the scope of the current project.
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The UUID of the Contact entity.'))
->setReadOnly(TRUE);
// Name field for the contact.
// We set display options for the view as well as the form.
// Users with correct privileges can change the view and edit configuration.
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of the Contact entity.'))
->setSettings(array(
'default_value' => '',
'max_length' => 255,
'text_processing' => 0,
))
->setDisplayOptions('view', array(
'label' => 'above',
'type' => 'string',
'weight' => -6,
))
->setDisplayOptions('form', array(
'type' => 'string_textfield',
'weight' => -6,
))
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('vieew', TRUE);
$fields['first_name'] = BaseFieldDefinition::create('string')
->setLabel(t('Address'))
->setDescription(t('The certificates of students field.'))
->setSetting('target_type', 'taxonomy_term')
->setSetting('handler', 'default')
->setSetting('handler_settings', ['target_bundles' => ['certificates' => 'certificates']])
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'entity_reference_label',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'options_select',
'weight' => 40,
])
->setDisplayConfigurable('form', TRUE)
->setDisplayConfigurable('view', TRUE);
//Image code start
$fields['image'] = BaseFieldDefinition::create('image')
->setLabel(t('Image'))
->setRequired(TRUE)
->setDisplayOptions('view', [
'label' => 'above',
'type' => 'image',
'weight' => 0,
])
->setDisplayOptions('form', [
'type' => 'image_image',
'weight' => 0,
]);
// Gender code start
$fields['gender'] = BaseFieldDefinition::create('list_string')
->setLabel(t('Gender'))
->setDescription(t('The gender of the Contact entity.'))
->setSettings(array(
'allowed_values' => array(
'female' => 'female',
'male' => 'male',
),
))
->setDisplayOptions('view', array(
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the entity was last edited.'));
return $fields;
}
}
?>
Step 7: Create a form for creating an Entity.
src/Form/ContactForm.php
<?php
namespace Drupal\content_entity_example\Form;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Language\Language;
use Drupal\Core\Form\FormStateInterface;/**
* Form controller for the content_entity_example entity edit forms.
*
* @ingroup content_entity_example
*/
class ContactForm extends ContentEntityForm { /**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
/* @var $entity \Drupal\content_entity_example\Entity\Contact */
$form = parent::buildForm($form, $form_state);
$entity = $this->entity; $form['langcode'] = array(
'#title' => $this->t('Language'),
'#type' => 'language_select', '#default_value' => $entity->getUntranslated()->language()->getId(),
'#languages' => Language::STATE_ALL,
);
return $form;
} /**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$status = parent::save($form, $form_state); $entity = $this->entity;
// dd($entity);
if ($status == SAVED_UPDATED) {
$this->messenger()
->addMessage($this->t('The contact %feed has been updated.', ['%feed' => $entity->toLink()->toString()]));
} else {
$this->messenger()
->addMessage($this->t('The contact %feed has been added.', ['%feed' => $entity->toLink()->toString()]));
} $form_state->setRedirectUrl($this->entity->toUrl('collection'));
return $status;
}
}
?>
a. Then create Contact DeleteForm.php file inside the Form folder
<?php
namespace Drupal\content_entity_example\Form;
use Drupal\Core\Entity\ContentEntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Provides a form for deleting a content_entity_example entity.
*
* @ingroup content_entity_example
*/
class ContactDeleteForm extends ContentEntityConfirmFormBase {
/**
* Returns the question to ask the user.
*
* @return string
* The form question. The page title will be set to this value.
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
}
/**
* Returns the route to go to if the user cancels the action.
*
* @return \Drupal\Core\Url
* A URL object.
*/
public function getCancelUrl() {
return new Url('entity.content_entity_example_contact.collection');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*
* Delete the entity and log the event. logger() replaces the watchdog.
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$entity = $this->getEntity();
$entity->delete();
$this->logger('content_entity_example')->notice('deleted %title.',
array(
'%title' => $this->entity->label(),
));
// Redirect to term list after delete.
$form_state->setRedirect('entity.content_entity_example_contact.collection');
}
}
?>
b. Then create ContactSettingsForm.php inside the Form folder.
<?php
namespace Drupal\content_entity_example\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Class ContentEntityExampleSettingsForm.
* @package Drupal\content_entity_example\Form
* @ingroup content_entity_example
*/
class ContactSettingsForm extends FormBase {
/**
* Returns a unique string identifying the form.
*
* @return string
* The unique string identifying the form.
*/
public function getFormId() {
return 'content_entity_example_settings';
}
c. Then create ContactListBuilder.php inside the src folder.
<?php
namespace Drupal\content_entity_example;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\media\Entity\Media;
/**
* Provides a list controller for content_entity_example_contact entity.
*
* @ingroup content_entity_example
*/
class ContactListBuilder extends EntityListBuilder {
/**
* {@inheritdoc}
*
* We override ::render() so that we can add our own content above the table.
* parent::render() is where EntityListBuilder creates the table using our
* buildHeader() and buildRow() implementations.
*/
public function render() {
$build['description'] = [
'#markup' => $this->t('Content Entity Example implements a Contacts model. These contacts are fieldable entities. You can manage the fields on the <a href="@adminlink">Contacts admin page</a>.', array(
'@adminlink' => Url::fromRoute('content_entity_example.contact_settings', [], ['absolute' => 'true'])->toString(),
)),
];
$build += parent::render();
return $build;
}
/**
* {@inheritdoc}
*
* Building the header and content lines for the contact list.
*
* Calling the parent::buildHeader() adds a column for the possible actions
* and inserts the 'edit' and 'delete' links as defined for the entity type.
*/
public function buildHeader() {
$header['id'] = $this->t('StudentID');
$header['name'] = $this->t('name');
$header['Address'] = $this->t('Address');
$header['fees'] = $this->t('fees');
$header['certificates'] = $this->t('certificates');
$header['gender'] = $this->t('Gender');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/* @var $entity \Drupal\content_entity_example\Entity\Contact */
$row['id'] = $entity->id();
$row['name'] = $entity->name->value;
$row['Address'] = $entity->first_name->value;
$row['fees'] = $entity->fees->value;
$tid = $entity->certificates->target_id;
$term_name = \Drupal\taxonomy\Entity\Term::load($tid)->get('name')->value;
$row['certificates'] = $term_name;
$fid = $entity->image->target_id;
$file = \Drupal\file\Entity\File::load($fid);
$path = $file->getFileUri();
$row['image'] = $path;
$row['gender'] = $entity->gender->value;
return $row + parent::buildRow($entity);
}
}
Step 8: Access Control Handler
Create src/ContactAccessControlHandler.php
<?php
namespace Drupal\content_entity_example;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;/**
* Access controller for the contact entity.
*
* @see \Drupal\content_entity_example\Entity\Contact.
*/
class ContactAccessControlHandler extends EntityAccessControlHandler { /**
* {@inheritdoc}
*
* Link the activities to the permissions. checkAccess is called with the
* $operation as defined in the routing.yml file.
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
switch ($operation) {
case 'view':
return AccessResult::allowedIfHasPermission($account, 'view contact entity'); case 'edit':
return AccessResult::allowedIfHasPermission($account, 'edit contact entity'); case 'delete':
return AccessResult::allowedIfHasPermission($account, 'delete contact entity');
}
return AccessResult::allowed();
} /**
* {@inheritdoc} *
* Separate from the checkAccess because the entity does not yet exist, it
* will be created during the 'add' process.
*/
protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
return AccessResult::allowedIfHasPermission($account, 'add contact entity');
}}
?>
Step 9: Create content_entity_example.permissions.yml
view contact entity:
title: 'View Contact entity'
add contact entity:
title: 'Add Contact entity'
edit contact entity:
title: 'Edit Contact Entity'
delete contact entity:
title: 'Delete Contact entity'
administer contact entity:
title: 'Administer Contact entity'
restrict access: TRUE
By following the steps you can create a Custom Entity
Let’s Wrap It Up
Custom entities play a pivotal role in the development of robust web applications and content management systems within the Drupal framework. By providing a standardized method for storing and managing data, custom entity types empower developers to tailor functionality to specific project requirements.
If you need assistance navigating the Drupal Custom entity or require expert guidance, LN Webworks, a Drupal development company, is here to help! Reach out to us today for personalized support and solutions.
Share This Post:
Author Information
LN Webworks
Your Drupal Solution PartnerLN Webworks have championed open-source technologies for nearly a decade, bringing advanced engineering capabilities and agile practices to some of the biggest names across media, entertainment, education, travel, hospitality, telecommunications and other industries.