|
| 1 | +--- |
| 2 | +name: itkdev-drupal |
| 3 | +description: Drupal development assistance for ITK Dev projects. Use when working with Drupal 10/11 codebases, creating modules/themes, running drush commands, auditing code quality, or managing configuration. Activates for projects with *.info.yml files or web/modules structure. |
| 4 | +--- |
| 5 | + |
| 6 | +# Drupal Development Guide |
| 7 | + |
| 8 | +You are assisting with Drupal 10/11 development. This skill covers code auditing, module/theme development, drush commands, and configuration management. |
| 9 | + |
| 10 | +## Project Detection |
| 11 | + |
| 12 | +Recognize Drupal projects by: |
| 13 | +- `*.info.yml` files in modules/themes |
| 14 | +- `web/modules` or `web/themes` directory structure |
| 15 | +- `composer.json` with `drupal/core` dependency |
| 16 | +- `docker-compose.yml` with ITK Dev Docker setup |
| 17 | +- `Taskfile.yml` with defined tasks |
| 18 | + |
| 19 | +## ITK Dev Docker Environment |
| 20 | + |
| 21 | +**IMPORTANT:** ITK Dev projects run in Docker containers. Never run commands directly on the host - always use `itkdev-docker-compose` or Taskfile tasks. |
| 22 | + |
| 23 | +### itkdev-docker-compose Commands |
| 24 | + |
| 25 | +The `itkdev-docker-compose` CLI wraps docker-compose and provides Drupal-specific commands: |
| 26 | + |
| 27 | +```bash |
| 28 | +# Drush (runs inside phpfpm container) |
| 29 | +itkdev-docker-compose drush cr # Clear cache |
| 30 | +itkdev-docker-compose drush cex -y # Export config |
| 31 | +itkdev-docker-compose drush cim -y # Import config |
| 32 | +itkdev-docker-compose drush updb -y # Run updates |
| 33 | + |
| 34 | +# Composer (runs inside phpfpm container) |
| 35 | +itkdev-docker-compose composer install |
| 36 | +itkdev-docker-compose composer require drupal/module_name |
| 37 | + |
| 38 | +# PHP (runs inside phpfpm container) |
| 39 | +itkdev-docker-compose php script.php |
| 40 | + |
| 41 | +# Database |
| 42 | +itkdev-docker-compose sql:cli # MySQL CLI |
| 43 | +itkdev-docker-compose sync:db # Sync database from remote |
| 44 | +itkdev-docker-compose sync:files # Sync files from remote |
| 45 | +itkdev-docker-compose sync # Sync both db and files |
| 46 | + |
| 47 | +# Utilities |
| 48 | +itkdev-docker-compose url # Print site URL |
| 49 | +itkdev-docker-compose open # Open site in browser |
| 50 | + |
| 51 | +# Any vendor/bin command |
| 52 | +itkdev-docker-compose vendor/bin/phpcs # Run PHP CodeSniffer |
| 53 | +itkdev-docker-compose vendor/bin/phpunit # Run PHPUnit |
| 54 | +``` |
| 55 | + |
| 56 | +### Taskfile.yml |
| 57 | + |
| 58 | +Projects typically have a `Taskfile.yml` defining common workflows. **Always check for and use Taskfile tasks first** before running raw commands. |
| 59 | + |
| 60 | +Common task patterns: |
| 61 | +```bash |
| 62 | +task # List available tasks |
| 63 | +task dev:setup # Initial project setup |
| 64 | +task dev:reset # Reset local environment |
| 65 | +task ci # Run CI checks (coding standards, tests) |
| 66 | +task ci:coding-standards |
| 67 | +task ci:phpunit |
| 68 | +task config:export # Export Drupal configuration |
| 69 | +task config:import # Import Drupal configuration |
| 70 | +``` |
| 71 | + |
| 72 | +**Convention:** If a Taskfile.yml exists, prefer `task <name>` over direct `itkdev-docker-compose` commands, as tasks often chain multiple commands and handle edge cases. |
| 73 | + |
| 74 | +## Xdebug |
| 75 | + |
| 76 | +ITK Dev Docker environments include Xdebug for step debugging PHP code. |
| 77 | + |
| 78 | +### Enabling Xdebug |
| 79 | + |
| 80 | +Xdebug is typically controlled via environment variables in the Docker setup: |
| 81 | + |
| 82 | +```bash |
| 83 | +# Check if Xdebug is enabled |
| 84 | +itkdev-docker-compose php -m | grep xdebug |
| 85 | + |
| 86 | +# Enable Xdebug (if using XDEBUG_MODE environment variable) |
| 87 | +# Add to docker-compose.override.yml or .env file: |
| 88 | +XDEBUG_MODE=debug |
| 89 | +``` |
| 90 | + |
| 91 | +Common `XDEBUG_MODE` values: |
| 92 | +- `off` - Disable Xdebug (default for performance) |
| 93 | +- `debug` - Enable step debugging |
| 94 | +- `coverage` - Enable code coverage analysis |
| 95 | +- `debug,coverage` - Enable both |
| 96 | + |
| 97 | +### IDE Configuration |
| 98 | + |
| 99 | +#### VS Code |
| 100 | + |
| 101 | +1. Install the **PHP Debug** extension by Xdebug |
| 102 | +2. Create `.vscode/launch.json`: |
| 103 | + |
| 104 | +```json |
| 105 | +{ |
| 106 | + "version": "0.2.0", |
| 107 | + "configurations": [ |
| 108 | + { |
| 109 | + "name": "Listen for Xdebug", |
| 110 | + "type": "php", |
| 111 | + "request": "launch", |
| 112 | + "port": 9003, |
| 113 | + "pathMappings": { |
| 114 | + "/app": "${workspaceFolder}" |
| 115 | + } |
| 116 | + } |
| 117 | + ] |
| 118 | +} |
| 119 | +``` |
| 120 | + |
| 121 | +#### PHPStorm |
| 122 | + |
| 123 | +1. Go to **Settings → PHP → Servers** |
| 124 | +2. Add a server with: |
| 125 | + - Host: Your local Docker URL (e.g., `project.docker.localhost`) |
| 126 | + - Port: 80 |
| 127 | + - Debugger: Xdebug |
| 128 | + - Path mappings: `/app` → project root |
| 129 | +3. Go to **Settings → PHP → Debug** |
| 130 | + - Debug port: 9003 |
| 131 | +4. Click **Start Listening for PHP Debug Connections** |
| 132 | + |
| 133 | +### Debugging Workflow |
| 134 | + |
| 135 | +1. Set breakpoints in your IDE |
| 136 | +2. Start listening for debug connections |
| 137 | +3. Trigger the code (via browser, drush, or CLI) |
| 138 | +4. Step through code using IDE controls |
| 139 | + |
| 140 | +### Debugging Drush Commands |
| 141 | + |
| 142 | +```bash |
| 143 | +# Run drush with Xdebug enabled |
| 144 | +XDEBUG_MODE=debug itkdev-docker-compose drush <command> |
| 145 | +``` |
| 146 | + |
| 147 | +### Troubleshooting |
| 148 | + |
| 149 | +- **Xdebug not connecting**: Check that `xdebug.client_host` points to your host machine (usually `host.docker.internal`) |
| 150 | +- **Wrong path mappings**: Ensure IDE path mappings match Docker volume mounts (typically `/app`) |
| 151 | +- **Performance issues**: Set `XDEBUG_MODE=off` when not debugging |
| 152 | + |
| 153 | +## Code Audit |
| 154 | + |
| 155 | +When reviewing Drupal code, check for: |
| 156 | + |
| 157 | +### Drupal Coding Standards |
| 158 | +- Follow [Drupal.org coding standards](https://www.drupal.org/docs/develop/standards) |
| 159 | +- Use proper namespacing: `Drupal\module_name\...` |
| 160 | +- Service-based architecture over procedural code |
| 161 | +- Dependency injection in classes |
| 162 | + |
| 163 | +### Security Review |
| 164 | +- **SQL Injection**: Use Database API with placeholders, never concatenate user input |
| 165 | +- **XSS Prevention**: Use `#markup` with `Xss::filter()`, `#plain_text`, or Twig auto-escaping |
| 166 | +- **CSRF Protection**: Use Form API with tokens for state-changing operations |
| 167 | +- **Access Control**: Implement proper permission checks |
| 168 | +- **Input Sanitization**: Use `\Drupal\Component\Utility\Html::escape()` |
| 169 | + |
| 170 | +### Debug Code Detection |
| 171 | +Flag and remove before commit: |
| 172 | +- `var_dump()`, `print_r()`, `dd()` |
| 173 | +- `dpm()`, `dsm()`, `kint()` |
| 174 | +- `\Drupal::logger()` with debug level in production code |
| 175 | + |
| 176 | +### Deprecated API Detection |
| 177 | + |
| 178 | +**Drupal 10 Deprecations** (removed in 11): |
| 179 | +- `drupal_set_message()` → use `\Drupal::messenger()->addMessage()` |
| 180 | +- `file_unmanaged_*` functions → use file system service |
| 181 | +- `entity_load()` → use entity type manager |
| 182 | +- `db_query()` → use Database service |
| 183 | + |
| 184 | +**Drupal 11 Changes**: |
| 185 | +- Symfony 7 compatibility required |
| 186 | +- PHP 8.3+ required |
| 187 | +- Check `*.info.yml` for `core_version_requirement` |
| 188 | + |
| 189 | +### Code Quality |
| 190 | +- SOLID principles in custom code |
| 191 | +- Single responsibility for services |
| 192 | +- Proper use of hooks vs. event subscribers |
| 193 | +- Render arrays over direct HTML |
| 194 | +- Configuration over hardcoded values |
| 195 | + |
| 196 | +## Module Development |
| 197 | + |
| 198 | +### Creating a New Module |
| 199 | + |
| 200 | +Basic structure: |
| 201 | +``` |
| 202 | +modules/custom/module_name/ |
| 203 | +├── module_name.info.yml |
| 204 | +├── module_name.module |
| 205 | +├── module_name.services.yml |
| 206 | +├── module_name.routing.yml |
| 207 | +├── module_name.permissions.yml |
| 208 | +├── src/ |
| 209 | +│ ├── Controller/ |
| 210 | +│ ├── Form/ |
| 211 | +│ ├── Plugin/ |
| 212 | +│ └── Service/ |
| 213 | +├── config/ |
| 214 | +│ ├── install/ |
| 215 | +│ └── schema/ |
| 216 | +└── templates/ |
| 217 | +``` |
| 218 | + |
| 219 | +### info.yml Template |
| 220 | +```yaml |
| 221 | +name: 'Module Name' |
| 222 | +type: module |
| 223 | +description: 'Module description' |
| 224 | +package: Custom |
| 225 | +core_version_requirement: ^10 || ^11 |
| 226 | +dependencies: |
| 227 | + - drupal:node |
| 228 | +``` |
| 229 | +
|
| 230 | +### Service Definition |
| 231 | +```yaml |
| 232 | +services: |
| 233 | + module_name.example_service: |
| 234 | + class: Drupal\module_name\Service\ExampleService |
| 235 | + arguments: ['@entity_type.manager', '@current_user'] |
| 236 | +``` |
| 237 | +
|
| 238 | +## Theme Development |
| 239 | +
|
| 240 | +### Creating a New Theme |
| 241 | +
|
| 242 | +Basic structure: |
| 243 | +``` |
| 244 | +themes/custom/theme_name/ |
| 245 | +├── theme_name.info.yml |
| 246 | +├── theme_name.libraries.yml |
| 247 | +├── theme_name.theme |
| 248 | +├── css/ |
| 249 | +├── js/ |
| 250 | +├── images/ |
| 251 | +└── templates/ |
| 252 | +``` |
| 253 | + |
| 254 | +### Theme info.yml Template |
| 255 | +```yaml |
| 256 | +name: 'Theme Name' |
| 257 | +type: theme |
| 258 | +description: 'Theme description' |
| 259 | +package: Custom |
| 260 | +core_version_requirement: ^10 || ^11 |
| 261 | +base theme: stable9 |
| 262 | +libraries: |
| 263 | + - theme_name/global-styling |
| 264 | +``` |
| 265 | +
|
| 266 | +## Drush Commands |
| 267 | +
|
| 268 | +**Always run drush via itkdev-docker-compose** (or use Taskfile tasks if available): |
| 269 | +
|
| 270 | +```bash |
| 271 | +# Cache |
| 272 | +itkdev-docker-compose drush cr # Clear all caches |
| 273 | +itkdev-docker-compose drush cc render # Clear render cache only |
| 274 | + |
| 275 | +# Configuration |
| 276 | +itkdev-docker-compose drush cex -y # Export configuration |
| 277 | +itkdev-docker-compose drush cim -y # Import configuration |
| 278 | +itkdev-docker-compose drush config:get system.site # Get specific config |
| 279 | + |
| 280 | +# Database |
| 281 | +itkdev-docker-compose drush sql:dump > backup.sql # Database backup |
| 282 | +itkdev-docker-compose sql:cli # Database CLI (direct command) |
| 283 | + |
| 284 | +# Updates |
| 285 | +itkdev-docker-compose drush updb -y # Run database updates |
| 286 | +itkdev-docker-compose drush entity:updates # Apply entity schema updates |
| 287 | + |
| 288 | +# Development |
| 289 | +itkdev-docker-compose drush en module_name -y # Enable module |
| 290 | +itkdev-docker-compose drush pmu module_name -y # Uninstall module |
| 291 | + |
| 292 | +# Generate (requires drush generators) |
| 293 | +itkdev-docker-compose drush generate module # Generate module scaffold |
| 294 | +itkdev-docker-compose drush generate controller # Generate controller |
| 295 | +``` |
| 296 | + |
| 297 | +## Configuration Management |
| 298 | + |
| 299 | +### Workflow |
| 300 | +1. Make changes in development environment |
| 301 | +2. Export: `task config:export` or `itkdev-docker-compose drush cex -y` |
| 302 | +3. Review changes in `config/sync/` |
| 303 | +4. Commit configuration files |
| 304 | +5. On deployment: `task config:import` or `itkdev-docker-compose drush cim -y` |
| 305 | + |
| 306 | +### Config Split |
| 307 | +For environment-specific configuration: |
| 308 | +- `config/sync/` - Shared configuration |
| 309 | +- `config/dev/` - Development overrides |
| 310 | +- `config/prod/` - Production overrides |
| 311 | + |
| 312 | +### Schema Definition |
| 313 | +Always define schema for custom configuration in `config/schema/module_name.schema.yml`: |
| 314 | +```yaml |
| 315 | +module_name.settings: |
| 316 | + type: config_object |
| 317 | + label: 'Module settings' |
| 318 | + mapping: |
| 319 | + enabled: |
| 320 | + type: boolean |
| 321 | + label: 'Enabled' |
| 322 | + api_key: |
| 323 | + type: string |
| 324 | + label: 'API Key' |
| 325 | +``` |
| 326 | +
|
| 327 | +## ITK Dev Conventions |
| 328 | +
|
| 329 | +When working on ITK Dev Drupal projects: |
| 330 | +- **Always use Docker**: Run all commands via `itkdev-docker-compose` or Taskfile tasks |
| 331 | +- **Check Taskfile.yml first**: Use `task` to list available tasks before running raw commands |
| 332 | +- Follow `itkdev-github-guidelines` for commits and PRs |
| 333 | +- Check for project-specific `CLAUDE.md` guidelines |
| 334 | +- Configuration should be exportable and version controlled |
| 335 | +- Custom modules go in `web/modules/custom/` |
| 336 | +- Custom themes go in `web/themes/custom/` |
| 337 | +- Run `task ci` (or equivalent) before creating PRs to ensure code quality |
0 commit comments