drupal-backend

Drupal Back End Specialist skill for custom module development, hooks, APIs, and PHP programming (Drupal 8-11+). Use when building custom modules, implementing hooks, working with entities, forms, plugins, or services.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "drupal-backend" with this command: npx skills add omedia/drupal-skill/omedia-drupal-skill-drupal-backend

Drupal Back End Development

Overview

Enable expert-level Drupal back end development capabilities. Provide comprehensive guidance for custom module development, PHP programming, API usage, hooks, plugins, services, and database operations for Drupal 8, 9, 10, and 11+.

When to Use This Skill

Invoke this skill when working with:

  • Custom module development: Creating new Drupal modules
  • Hooks: Implementing Drupal hooks to alter system behavior
  • Controllers & routing: Building custom pages and routes
  • Forms: Creating configuration or custom forms
  • Entities: Working with content entities or config entities
  • Plugins: Building blocks, field types, formatters, or other plugins
  • Services: Creating reusable business logic services
  • Database operations: Custom queries and schema definitions
  • APIs: Entity API, Form API, Database API, Plugin API

Core Capabilities

1. Custom Module Development

Create complete, standards-compliant Drupal modules:

Quick start workflow:

  1. Use module template from assets/module-template/
  2. Replace MODULENAME with machine name (lowercase, underscores)
  3. Replace MODULELABEL with human-readable name
  4. Implement functionality using Drupal APIs
  5. Enable module and test: ddev drush en mymodule -y

Module structure:

  • .info.yml - Module metadata and dependencies
  • .module - Hook implementations
  • .routing.yml - Route definitions
  • .services.yml - Service definitions
  • .permissions.yml - Custom permissions
  • src/ - PHP classes (PSR-4 autoloading)
  • config/ - Configuration files

Reference documentation:

  • references/module_structure.md - Complete module patterns
  • references/hooks.md - Hook implementations

2. Hooks System

Implement hooks to alter Drupal behavior:

Common hooks:

  • hook_entity_presave() - Modify entities before saving
  • hook_entity_insert/update/delete() - React to entity changes
  • hook_form_alter() - Modify any form
  • hook_node_access() - Control node access
  • hook_cron() - Perform periodic tasks
  • hook_install/uninstall() - Module installation tasks

Hook pattern:

function MODULENAME_hook_name($param1, &$param2) {
  // Implementation
}

Best practices:

  • Implement hooks in .module file only
  • Use proper type hints
  • Document with PHPDoc blocks
  • Check for entity types before processing
  • Be mindful of performance

3. Controllers & Routing

Create custom pages and endpoints:

Route definition (.routing.yml):

mymodule.example:
  path: '/example/{param}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\ExampleController::content'
    _title: 'Example Page'
  requirements:
    _permission: 'access content'
    param: \d+

Controller pattern:

namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase;

class ExampleController extends ControllerBase {
  public function content() {
    return ['#markup' => $this->t('Hello!')];
  }
}

With dependency injection:

public function __construct(EntityTypeManagerInterface $entity_type_manager) {
  $this->entityTypeManager = $entity_type_manager;
}

public static function create(ContainerInterface $container) {
  return new static($container->get('entity_type.manager'));
}

4. Forms API

Build configuration and custom forms:

Configuration form:

class SettingsForm extends ConfigFormBase {
  protected function getEditableConfigNames() {
    return ['mymodule.settings'];
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('mymodule.settings');
    
    $form['api_key'] = [
      '#type' => 'textfield',
      '#title' => $this->t('API Key'),
      '#default_value' => $config->get('api_key'),
    ];
    
    return parent::buildForm($form, $form_state);
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->config('mymodule.settings')
      ->set('api_key', $form_state->getValue('api_key'))
      ->save();
    parent::submitForm($form, $form_state);
  }
}

Form validation:

public function validateForm(array &$form, FormStateInterface $form_state) {
  if (strlen($form_state->getValue('field')) < 5) {
    $form_state->setErrorByName('field', $this->t('Too short.'));
  }
}

5. Entity API

Work with content and configuration entities:

Loading entities:

// Load single entity
$node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);

// Load multiple entities
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple([1, 2, 3]);

// Load by properties
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
  'type' => 'article',
  'status' => 1,
]);

Creating/saving entities:

$node = \Drupal::entityTypeManager()->getStorage('node')->create([
  'type' => 'article',
  'title' => 'My Article',
  'body' => ['value' => 'Content', 'format' => 'basic_html'],
]);
$node->save();

Entity queries:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'article')
  ->condition('status', 1)
  ->accessCheck(TRUE)
  ->sort('created', 'DESC')
  ->range(0, 10);
$nids = $query->execute();

6. Plugin System

Create custom plugins (blocks, fields, etc.):

Block plugin:

/**
 * @Block(
 *   id = "mymodule_custom_block",
 *   admin_label = @Translation("Custom Block"),
 * )
 */
class CustomBlock extends BlockBase {
  public function build() {
    return ['#markup' => 'Block content'];
  }
  
  public function blockForm($form, FormStateInterface $form_state) {
    $form['setting'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Setting'),
      '#default_value' => $this->configuration['setting'] ?? '',
    ];
    return $form;
  }
  
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['setting'] = $form_state->getValue('setting');
  }
}

7. Services & Dependency Injection

Create reusable services:

Service definition (.services.yml):

services:
  mymodule.custom_service:
    class: Drupal\mymodule\Service\CustomService
    arguments: ['@entity_type.manager', '@current_user']

Service class:

namespace Drupal\mymodule\Service;

class CustomService {
  protected $entityTypeManager;
  protected $currentUser;

  public function __construct(EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $current_user) {
    $this->entityTypeManager = $entity_type_manager;
    $this->currentUser = $current_user;
  }

  public function doSomething() {
    // Business logic
  }
}

8. Database Operations

Execute custom queries:

Using Database API:

$database = \Drupal::database();

// Select query
$query = $database->select('node_field_data', 'n')
  ->fields('n', ['nid', 'title'])
  ->condition('type', 'article')
  ->condition('status', 1)
  ->range(0, 10);
$results = $query->execute()->fetchAll();

// Insert
$database->insert('mymodule_table')
  ->fields(['name' => 'Example', 'value' => 123])
  ->execute();

// Update
$database->update('mymodule_table')
  ->fields(['value' => 456])
  ->condition('name', 'Example')
  ->execute();

Schema definition (.install file):

function mymodule_schema() {
  $schema['mymodule_table'] = [
    'fields' => [
      'id' => ['type' => 'serial', 'not null' => TRUE],
      'name' => ['type' => 'varchar', 'length' => 255, 'not null' => TRUE],
      'value' => ['type' => 'int', 'not null' => TRUE, 'default' => 0],
    ],
    'primary key' => ['id'],
    'indexes' => ['name' => ['name']],
  ];
  return $schema;
}

Development Workflow

Creating a Custom Module

  1. Scaffold the module:

    cp -r assets/module-template/ /path/to/drupal/modules/custom/mymodule/
    cd /path/to/drupal/modules/custom/mymodule/
    mv MODULENAME.info.yml mymodule.info.yml
    mv MODULENAME.module mymodule.module
    mv MODULENAME.routing.yml mymodule.routing.yml
    
  2. Update module files:

    • Replace MODULENAME with machine name
    • Replace MODULELABEL with readable name
    • Update .info.yml dependencies
    • Customize controller, routes, logic
  3. Enable and test:

    ddev drush en mymodule -y
    ddev drush cr
    
  4. Develop iteratively:

    • Implement functionality
    • Clear cache: ddev drush cr
    • Test thoroughly
    • Check logs: ddev drush watchdog:tail

Standard Development Workflow

  1. Plan: Identify what APIs/hooks you need
  2. Code: Implement using Drupal APIs
  3. Test: Enable module, clear cache, test functionality
  4. Debug: Check logs, use Xdebug if needed
  5. Refine: Optimize, add tests, improve code quality

Best Practices

Module Development

  1. PSR-4 autoloading: Follow namespace conventions
  2. Dependency injection: Use DI in classes
  3. Hooks: Implement only in .module files
  4. Type hints: Use proper PHP type declarations
  5. Documentation: Add PHPDoc blocks
  6. Testing: Write automated tests

Code Quality

  1. Drupal coding standards: Follow PHPCS rules
  2. Security: Validate input, sanitize output, check permissions
  3. Performance: Cache when possible, optimize queries
  4. Accessibility: Ensure forms and pages are accessible
  5. Internationalization: Use $this->t() for all strings

API Usage

  1. Entity API: Prefer entity operations over direct DB queries
  2. Configuration: Use Config API for settings
  3. State API: Use for temporary/non-exportable data
  4. Cache API: Implement caching for expensive operations
  5. Queue API: Use for long-running tasks

Common Patterns

Event Subscriber

class MyModuleSubscriber implements EventSubscriberInterface {
  public static function getSubscribedEvents() {
    return [KernelEvents::REQUEST => ['onRequest', 0]];
  }

  public function onRequest(RequestEvent $event) {
    // Handle event
  }
}

Custom Permission

# mymodule.permissions.yml
administer mymodule:
  title: 'Administer My Module'
  restrict access: true

Custom Access Check

class CustomAccessCheck implements AccessInterface {
  public function access(AccountInterface $account) {
    return AccessResult::allowedIf($account->hasPermission('access content'));
  }
}

Troubleshooting

Module Not Appearing

  • Check .info.yml syntax
  • Verify core_version_requirement
  • Clear cache: ddev drush cr

Hooks Not Working

  • Verify hook name is correct
  • Clear cache after adding hooks
  • Check module weight if hook order matters

Class Not Found

  • Verify PSR-4 namespace matches directory
  • Clear cache to rebuild class registry
  • Check autoloading with composer dump-autoload

Database Errors

  • Check schema definition syntax
  • Run ddev drush updb after schema changes
  • Verify table/column names in queries

Resources

Reference Documentation

  • references/hooks.md - Common hooks with examples

    • Entity hooks
    • Form hooks
    • Node hooks
    • Installation hooks
    • Token hooks
  • references/module_structure.md - Module patterns

    • Controllers and routing
    • Forms (config and custom)
    • Plugins (blocks, fields, etc.)
    • Services and DI
    • Event subscribers

Asset Templates

  • assets/module-template/ - Module scaffold
    • .info.yml metadata
    • .module hook file
    • .routing.yml routes
    • Example controller

Version Compatibility

Drupal 8 vs 9 vs 10 vs 11

  • Core APIs: Largely consistent across versions
  • PHP requirements: 8.x (7.0+), 9.x (7.3+), 10.x (8.1+), 11.x (8.3+)
  • Deprecations: Check change records when upgrading
  • Symfony: Different Symfony versions in each Drupal version

See Also

  • drupal-frontend - Theme development, Twig templates
  • drupal-tooling - DDEV and Drush development tools
  • Drupal API - Official API documentation

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

drupal-frontend

No summary provided by upstream source.

Repository SourceNeeds Review
General

drupal-tooling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

openclaw-version-monitor

监控 OpenClaw GitHub 版本更新,获取最新版本发布说明,翻译成中文, 并推送到 Telegram 和 Feishu。用于:(1) 定时检查版本更新 (2) 推送版本更新通知 (3) 生成中文版发布说明

Archived SourceRecently Updated