Base Classes and Inheritance
When to Use
When you have multiple classes of the same type (entities, forms, blocks, controllers) that share common patterns or need to override specific base functionality.
Core Base Classes
| Base Class | Extends From | Use When |
|---|---|---|
ContentEntityBase |
EntityBase |
Creating content entity types (nodes, users, custom entities) |
ConfigEntityBase |
EntityBase |
Creating config entity types (views, field configs) |
FormBase |
- (implements FormInterface) |
Creating forms (uses traits for common services) |
ConfigFormBase |
FormBase |
Creating admin forms that save to config |
BlockBase |
PluginBase |
Creating block plugins |
PluginBase |
- | Creating any plugin type |
ControllerBase |
- | Creating controllers (uses traits) |
Decision
| If you need... | Use... | Why |
|---|---|---|
| Custom content entity | Extend ContentEntityBase |
Field API support, revisions, translations |
| Custom admin form | Extend ConfigFormBase |
Built-in config save/load methods |
| Custom block | Extend BlockBase |
Context, caching, plugin scaffolding |
| Multiple similar custom forms | Create shared base class extending FormBase |
Reuse common methods, eliminate duplication |
| Override parent constructor | Call parent::__construct() AND override create() |
Maintain proper DI chain |
Pattern
// Extending core base class
namespace Drupal\mymodule\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
class MyEntity extends ContentEntityBase {
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setRequired(TRUE);
return $fields;
}
}
// Creating project-specific base class
namespace Drupal\mymodule\Form;
use Drupal\Core\Form\FormBase;
abstract class MyProjectFormBase extends FormBase {
// Shared validation logic
protected function validateEmail(string $email): bool {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
// Shared submission patterns
protected function logFormSubmission(string $form_id): void {
$this->getLogger('mymodule')->info('Form @id submitted', ['@id' => $form_id]);
}
}
Reference: core/lib/Drupal/Core/Entity/ContentEntityBase.php, core/lib/Drupal/Core/Form/FormBase.php, core/lib/Drupal/Core/Block/BlockBase.php
Common Mistakes
- Deep inheritance hierarchies — Keep inheritance shallow (2-3 levels max); prefer composition via traits or services
- Overriding constructors without calling parent — Always call
parent::__construct()and overridecreate()method for DI - Creating base classes prematurely — Wait until you have 3+ similar classes before creating a shared base class
- God base classes with too many methods — Keep base classes focused; use traits for orthogonal concerns
- Forgetting to extend from correct core base —
FormBasefor forms,ContentEntityBasefor content entities,BlockBasefor blocks
See Also
- Services for Shared Logic
- Traits for Cross-Cutting Concerns
- Reference: Extending Drupal Base Classes Without Overriding Constructors
- Reference:
core/lib/Drupal/Core/Entity/ContentEntityBase.php