Drupal 12 introduces one of the biggest improvements to the Database Schema API in years. The traditional nested array structure used to define database schemas is being replaced with a modern, object-oriented API built around immutable value objects.
This change makes schema definitions easier to validate, safer to extend, and opens the door for future database-specific capabilities without sacrificing Drupal's database abstraction.
Why Was This Changed?
For nearly two decades, Drupal developers have defined database tables using large nested arrays inside hook_schema() implementations.
While this approach has served Drupal well, it has several drawbacks:
- No type safety
- Difficult validation
- Limited IDE autocompletion
- Hard to extend without breaking compatibility
- Database-specific options mixed with abstract schema definitions
Drupal 12 replaces these arrays with dedicated value objects located in the Drupal\Core\Database\SchemaDefinition namespace.
The new API is immutable, strongly typed, and much easier for both developers and static analysis tools to understand.
Before: Array-Based Schema Definitions
A typical table definition looked like this:
$schema['config'] = [
'description' => 'The base table for configuration data.',
'fields' => [
'collection' => [
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
'name' => [
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
'default' => '',
],
],
'primary key' => ['collection', 'name'],
];
Although familiar, this format relied on string keys and conventions that were difficult to validate.
After: Object-Oriented Schema Definitions
The same table is now represented using immutable value objects:
new Table(
name: 'config',
description: 'The base table for configuration data.',
columns: [
new Column(
name: 'collection',
type: ColumnType::VarcharAscii,
length: 255,
notNull: TRUE,
default: new StringValue(''),
),
new Column(
name: 'name',
type: ColumnType::VarcharAscii,
length: 255,
notNull: TRUE,
default: new StringValue(''),
),
],
primaryKey: new PrimaryKey(['collection', 'name']),
);
Several improvements are immediately noticeable:
- Strongly typed classes instead of nested arrays
- PHP named arguments improve readability
- IDE autocompletion works much better
- Invalid properties can be detected much earlier
- Immutable objects eliminate accidental modification
hook_schema() Has Changed
The return type of hook_schema() is no longer an array.
Drupal 11 and Earlier
function mymodule_schema(): array {
$schema['my_table'] = [
// ...
];
return $schema;
}
Drupal 12
function mymodule_schema(): Schema {
return new Schema(
type: SchemaDefinitionType::Module,
name: 'mymodule',
tables: [
new Table(
name: 'my_table',
// ...
),
],
);
}
Instead of returning an associative array, modules now return a Schema object containing one or more Table definitions.
Storage Schema Definitions Also Change
Custom storage handlers that previously returned arrays must now return Schema objects.
Creating tables has also changed.
Previous API
$this->connection
->schema()
->createTable($table_name, $schema_definition);
New API
$this->connection
->schema()
->createSchemaFromDefinition(
$this->schemaDefinition()
);
Likewise, schemaDefinition() now returns a Schema object instead of an array.
Benefits for Module Developers
The new API provides several practical advantages:
- Better IDE support
- Strong typing
- Immutable schema definitions
- Automatic validation of properties
- Easier future API evolution
- Cleaner, more readable code
- Improved maintainability
Because the API uses PHP objects instead of magic array keys, refactoring becomes much safer and static analysis tools can detect many mistakes before code is executed.
Future Possibilities
Perhaps the most exciting aspect of this redesign is what it enables in future Drupal versions.
The old schema arrays contained database-specific field types such as varchar_ascii, making true database abstraction difficult.
With the new architecture, Drupal can separate generic schema definitions from database-specific implementations. This opens the possibility for database drivers to expose advanced features including:
- PostgreSQL GIN indexes
- PostgreSQL GiST indexes
- Vendor-specific index options
- Improved schema introspection
- Better migration tooling
- More powerful validation
The core schema definition remains database-independent while drivers can implement advanced capabilities behind the scenes.
Migration Considerations
This API change lands in Drupal 12 and is not backward compatible.
Module developers will eventually need to update:
hook_schema()implementations- Custom storage handlers
- Installation schemas
- Test schemas
- Any code that dynamically creates database tables
Fortunately, the new API closely mirrors the concepts of the old one, making migration mostly mechanical.
Final Thoughts
The Database Schema Definition API is a significant modernization of one of Drupal's oldest subsystems.
Replacing fragile nested arrays with immutable value objects brings stronger typing, better tooling support, cleaner code, and a much more extensible architecture.
While module developers will need to update their schema definitions when adopting Drupal 12, the long-term benefits far outweigh the migration effort. This change lays a solid foundation for future database innovations while making everyday schema definitions easier to write and maintain.
Comments