Drupal 11 Context
· 4.4 KiB · Text
Raw
# Drupal 11 Content Management System
## Introduction
Drupal 11 is a modern, enterprise-grade content management framework built on PHP 8.3+ and Symfony 7 components. Unlike Drupal 7's procedural architecture, Drupal 11 utilizes a fully Object-Oriented Programming (OOP) architecture, leveraging a Dependency Injection Container, Composer for package management, and a plugin-based system for extensibility.
At its core, Drupal 11 treats content as **Entities** (typed objects). It separates configuration (YAML-based) from content (Database-based). The system relies on PSR-4 autoloading, namespaced classes, and event subscribers. While legacy hooks exist, Drupal 11 introduces **Hook Attributes** and emphasizes Event Subscribers. Output is handled via the Twig templating engine, and routing is defined via YAML files mapping paths to Controller classes.
## APIs and Key Functions
### Entity API - Content CRUD Operations
The Entity API is the primary method for interacting with data. Nodes, Users, Terms, and custom entities are all instances of classes implementing `EntityInterface`. Direct database access for content is discouraged.
```php
use Drupal\node\Entity\Node;
use Drupal\Core\File\FileSystemInterface;
// Create a new article node
$node = Node::create([
'type' => 'article',
'title' => 'Getting Started with Drupal 11',
'langcode' => 'en',
'uid' => 1, // Author ID
'status' => Node::PUBLISHED,
'body' => [
'value' => 'Drupal 11 is a powerful framework...',
'format' => 'full_html',
],
// Entity reference (Taxonomy) uses target_id
'field_tags' => [
['target_id' => 5],
['target_id' => 8],
],
]);
// Save the node
$node->save();
echo "Created node with ID: " . $node->id();
// Load an existing node
$node = Node::load(1);
if ($node instanceof Node) {
// Access fields using Magic Getters or get()
echo $node->getTitle();
echo $node->body->value;
echo $node->get('body')->value; // more explicit
// Access Entity Reference fields
if (!$node->get('field_image')->isEmpty()) {
$entity = $node->field_image->entity;
$url = $entity->createFileUrl();
}
}
// Load multiple nodes
$nids = [1, 2, 3, 4, 5];
$nodes = Node::loadMultiple($nids);
foreach ($nodes as $node) {
echo $node->label() . "\n";
}
// Update a node
$node = Node::load(1);
$node->setTitle('Updated Article Title');
$node->set('body', ['value' => 'Updated content...', 'format' => 'basic_html']);
$node->field_category[] = ['target_id' => 10]; // Append value
$node->setNewRevision(TRUE);
$node->revision_log = 'Updated article info';
$node->save();
// Delete a node
$node = Node::load(1);
$node->delete();
// Delete multiple nodes (loading them first is best practice to trigger hooks)
$nodes = Node::loadMultiple([5, 6, 7]);
$storage_handler = \Drupal::entityTypeManager()->getStorage('node');
$storage_handler->delete($nodes);
// Get node by properties (using Entity Storage)
$nodes = \Drupal::entityTypeManager()
->getStorage('node')
->loadByProperties(['title' => 'My Article', 'status' => 1]);
$node = reset($nodes);
```
### Entity Query API - Complex Content Queries
Replaces `EntityFieldQuery`. Used to find entity IDs based on field values or properties. Always uses fluent interfaces.
```php
// Basic node query
$query = \Drupal::entityQuery('node')
->condition('type', 'article')
->condition('status', 1)
->condition('field_category.entity.name', 'Tech') // Query referenced entity data
->sort('created', 'DESC')
->range(0, 10)
->accessCheck(TRUE); // MANDATORY in D10/11
$nids = $query->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
// Complex conditions (AND/OR groups)
$query = \Drupal::entityQuery('node')
->condition('status', 1)
->accessCheck(TRUE);
$group = $query->orConditionGroup()
->condition('field_price', 100, '>')
->condition('field_stock', 0, '>');
$query->condition($group);
$result = $query->execute();
// Date range query
$query = \Drupal::entityQuery('node')
->condition('field_date.value', ['2024-01-01', '2024-12-31'], 'BETWEEN')
->accessCheck(TRUE)
->execute();
// Count results only
$count = \Drupal::entityQuery('node')
->condition('type', 'article')
->accessCheck(TRUE)
->count()
->execute();
```
### Database API
Used for non-entity data or performance-critical reporting. Access is typically handled via Dependency Injection (`@database` service), but static access exists for legacy support or scripts.
```php
use Drupal\Core\Database\Database;
$connection = \Drupal::database();
//
| 1 | # Drupal 11 Content Management System |
| 2 | |
| 3 | ## Introduction |
| 4 | |
| 5 | Drupal 11 is a modern, enterprise-grade content management framework built on PHP 8.3+ and Symfony 7 components. Unlike Drupal 7's procedural architecture, Drupal 11 utilizes a fully Object-Oriented Programming (OOP) architecture, leveraging a Dependency Injection Container, Composer for package management, and a plugin-based system for extensibility. |
| 6 | |
| 7 | At its core, Drupal 11 treats content as **Entities** (typed objects). It separates configuration (YAML-based) from content (Database-based). The system relies on PSR-4 autoloading, namespaced classes, and event subscribers. While legacy hooks exist, Drupal 11 introduces **Hook Attributes** and emphasizes Event Subscribers. Output is handled via the Twig templating engine, and routing is defined via YAML files mapping paths to Controller classes. |
| 8 | |
| 9 | ## APIs and Key Functions |
| 10 | |
| 11 | ### Entity API - Content CRUD Operations |
| 12 | |
| 13 | The Entity API is the primary method for interacting with data. Nodes, Users, Terms, and custom entities are all instances of classes implementing `EntityInterface`. Direct database access for content is discouraged. |
| 14 | |
| 15 | ```php |
| 16 | use Drupal\node\Entity\Node; |
| 17 | use Drupal\Core\File\FileSystemInterface; |
| 18 | |
| 19 | // Create a new article node |
| 20 | $node = Node::create([ |
| 21 | 'type' => 'article', |
| 22 | 'title' => 'Getting Started with Drupal 11', |
| 23 | 'langcode' => 'en', |
| 24 | 'uid' => 1, // Author ID |
| 25 | 'status' => Node::PUBLISHED, |
| 26 | 'body' => [ |
| 27 | 'value' => 'Drupal 11 is a powerful framework...', |
| 28 | 'format' => 'full_html', |
| 29 | ], |
| 30 | // Entity reference (Taxonomy) uses target_id |
| 31 | 'field_tags' => [ |
| 32 | ['target_id' => 5], |
| 33 | ['target_id' => 8], |
| 34 | ], |
| 35 | ]); |
| 36 | |
| 37 | // Save the node |
| 38 | $node->save(); |
| 39 | echo "Created node with ID: " . $node->id(); |
| 40 | |
| 41 | // Load an existing node |
| 42 | $node = Node::load(1); |
| 43 | if ($node instanceof Node) { |
| 44 | // Access fields using Magic Getters or get() |
| 45 | echo $node->getTitle(); |
| 46 | echo $node->body->value; |
| 47 | echo $node->get('body')->value; // more explicit |
| 48 | |
| 49 | // Access Entity Reference fields |
| 50 | if (!$node->get('field_image')->isEmpty()) { |
| 51 | $entity = $node->field_image->entity; |
| 52 | $url = $entity->createFileUrl(); |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | // Load multiple nodes |
| 57 | $nids = [1, 2, 3, 4, 5]; |
| 58 | $nodes = Node::loadMultiple($nids); |
| 59 | foreach ($nodes as $node) { |
| 60 | echo $node->label() . "\n"; |
| 61 | } |
| 62 | |
| 63 | // Update a node |
| 64 | $node = Node::load(1); |
| 65 | $node->setTitle('Updated Article Title'); |
| 66 | $node->set('body', ['value' => 'Updated content...', 'format' => 'basic_html']); |
| 67 | $node->field_category[] = ['target_id' => 10]; // Append value |
| 68 | $node->setNewRevision(TRUE); |
| 69 | $node->revision_log = 'Updated article info'; |
| 70 | $node->save(); |
| 71 | |
| 72 | // Delete a node |
| 73 | $node = Node::load(1); |
| 74 | $node->delete(); |
| 75 | |
| 76 | // Delete multiple nodes (loading them first is best practice to trigger hooks) |
| 77 | $nodes = Node::loadMultiple([5, 6, 7]); |
| 78 | $storage_handler = \Drupal::entityTypeManager()->getStorage('node'); |
| 79 | $storage_handler->delete($nodes); |
| 80 | |
| 81 | // Get node by properties (using Entity Storage) |
| 82 | $nodes = \Drupal::entityTypeManager() |
| 83 | ->getStorage('node') |
| 84 | ->loadByProperties(['title' => 'My Article', 'status' => 1]); |
| 85 | $node = reset($nodes); |
| 86 | ``` |
| 87 | |
| 88 | ### Entity Query API - Complex Content Queries |
| 89 | |
| 90 | Replaces `EntityFieldQuery`. Used to find entity IDs based on field values or properties. Always uses fluent interfaces. |
| 91 | |
| 92 | ```php |
| 93 | // Basic node query |
| 94 | $query = \Drupal::entityQuery('node') |
| 95 | ->condition('type', 'article') |
| 96 | ->condition('status', 1) |
| 97 | ->condition('field_category.entity.name', 'Tech') // Query referenced entity data |
| 98 | ->sort('created', 'DESC') |
| 99 | ->range(0, 10) |
| 100 | ->accessCheck(TRUE); // MANDATORY in D10/11 |
| 101 | |
| 102 | $nids = $query->execute(); |
| 103 | $nodes = \Drupal\node\Entity\Node::loadMultiple($nids); |
| 104 | |
| 105 | // Complex conditions (AND/OR groups) |
| 106 | $query = \Drupal::entityQuery('node') |
| 107 | ->condition('status', 1) |
| 108 | ->accessCheck(TRUE); |
| 109 | |
| 110 | $group = $query->orConditionGroup() |
| 111 | ->condition('field_price', 100, '>') |
| 112 | ->condition('field_stock', 0, '>'); |
| 113 | |
| 114 | $query->condition($group); |
| 115 | $result = $query->execute(); |
| 116 | |
| 117 | // Date range query |
| 118 | $query = \Drupal::entityQuery('node') |
| 119 | ->condition('field_date.value', ['2024-01-01', '2024-12-31'], 'BETWEEN') |
| 120 | ->accessCheck(TRUE) |
| 121 | ->execute(); |
| 122 | |
| 123 | // Count results only |
| 124 | $count = \Drupal::entityQuery('node') |
| 125 | ->condition('type', 'article') |
| 126 | ->accessCheck(TRUE) |
| 127 | ->count() |
| 128 | ->execute(); |
| 129 | ``` |
| 130 | |
| 131 | ### Database API |
| 132 | |
| 133 | Used for non-entity data or performance-critical reporting. Access is typically handled via Dependency Injection (`@database` service), but static access exists for legacy support or scripts. |
| 134 | |
| 135 | ```php |
| 136 | use Drupal\Core\Database\Database; |
| 137 | |
| 138 | $connection = \Drupal::database(); |
| 139 | |
| 140 | // |