# 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(); //