From drupal-core
Comprehensive guide for managing Drupal contributed modules via Composer, including updates, patches, version compatibility, and Drupal 11 upgrades. Use when updating modules or resolving dependency issues.
npx claudepluginhub ajv009/drupal-devkitThis skill uses the workspace's default tool permissions.
```bash
Safely manages Composer dependencies: audits vulnerabilities, applies patch/minor/major update strategies, reviews changelogs, maintains lock files, resolves conflicts, configures Dependabot/Renovate.
Provides patterns for Drupal 11 OOP hooks with #[Hook] attributes, form alters, entity hooks, and legacy bridges for Drupal 10/11. Use when implementing hooks, form alterations, or event subscribers.
Prevents silent decimal mismatch bugs in EVM ERC-20 tokens via runtime decimals lookup, chain-aware caching, bridged-token handling, and normalization. For DeFi bots, dashboards using Python/Web3, TypeScript/ethers, Solidity.
Share bugs, ideas, or general feedback.
# Update a single module
composer require drupal/module_name --with-all-dependencies
# Update to specific version
composer require drupal/module_name:^3.0 --with-all-dependencies
# Update multiple modules
composer require drupal/module_a drupal/module_b --with-all-dependencies
# After any update, ALWAYS run database updates
drush updb -y
# Clear cache if needed
drush cr
# CRITICAL: Test by visiting pages to check for fatal errors
# Visit at least one page that uses the updated module
When upgrading to a new major version (e.g., 2.x → 3.x):
https://www.drupal.org/project/issues/MODULE_NAME?categories=AllThree methods to check if a module is D11 compatible (in order of preference):
# Check the module's .info.yml file for core_version_requirement
cat docroot/modules/contrib/MODULE_NAME/MODULE_NAME.info.yml | grep core_version_requirement
What to look for:
core_version_requirement: ^9.5 || ^10 || ^11 # ✅ D11 compatible
core_version_requirement: ^8 || ^9 || ^10 || ^11 # ✅ D11 compatible
core_version_requirement: ^9 || ^10 # ❌ Not D11 compatible yet
Example:
$ cat docroot/modules/contrib/admin_toolbar/admin_toolbar.info.yml | grep core_version
core_version_requirement: ^9.5 || ^10 || ^11
# ✅ This module declares D11 support!
# Check what versions are available and their constraints
composer show drupal/MODULE_NAME --all | grep -A5 "^versions"
# Check currently installed version
composer show drupal/MODULE_NAME | grep versions
What to look for:
Only use as fallback when above methods aren't conclusive.
https://www.drupal.org/project/MODULE_NAME
Look for:
Important Notes:
^11 support, module maintainer says it worksReal-World Examples:
# admin_toolbar - Already D11 compatible
$ cat docroot/modules/contrib/admin_toolbar/admin_toolbar.info.yml | grep core_version
core_version_requirement: ^9.5 || ^10 || ^11
# But upgrade_status shows warnings about _drupal_flush_css_js()
# This is a FALSE POSITIVE - module handles it with version checks
# audiofield - Already D11 compatible
$ cat docroot/modules/contrib/audiofield/audiofield.info.yml | grep core_version
core_version_requirement: ^8 || ^9 || ^10 || ^11
# Has deprecation warnings but maintainer declares D11 support
The mglaman/composer-drupal-lenient plugin allows installing modules that haven't updated their version requirements yet.
{
"require": {
"mglaman/composer-drupal-lenient": "^1.0"
},
"config": {
"allow-plugins": {
"mglaman/composer-drupal-lenient": true
}
},
"extra": {
"drupal-lenient": {
"allowed-list": [
"drupal/module_name",
"drupal/another_module"
]
}
}
}
# Add module to allowed-list, then install
composer require drupal/module_name --with-all-dependencies
{
"require": {
"cweagans/composer-patches": "^1.7"
},
"config": {
"allow-plugins": {
"cweagans/composer-patches": true
}
},
"extra": {
"patches": {
"drupal/module_name": {
"Description of patch": "https://www.drupal.org/files/issues/2024-01-15/module-issue-1234567-8.patch",
"Local patch": "patches/custom-fix.patch"
}
},
"enable-patching": true,
"patchLevel": {
"drupal/core": "-p2"
}
}
}
Issue Queue Search: https://www.drupal.org/project/issues/MODULE_NAME?categories=All
Patch Naming Convention:
module-issue-NODEID-COMMENT.patchaudiofield-d11-3432063-12.patchdrupal.org/node/NODEID)When Existing Patches Fail After Update:
3432063 from above)https://www.drupal.org/node/3432063# Method 1: Git diff
cd docroot/modules/contrib/module_name
# Make your changes
git diff > /path/to/project/patches/module-custom-fix.patch
# Method 2: diff command
diff -Naur original/file.php modified/file.php > patches/module-fix.patch
# Apply in composer.json
{
"extra": {
"patches": {
"drupal/module_name": {
"Custom fix description": "patches/module-custom-fix.patch"
}
}
}
}
# Install with patches
composer install
# If patches fail, composer will error
# Update or remove failing patches, then retry
composer install
# Force re-patch
composer update drupal/module_name --with-all-dependencies
# Scan all modules
drush upgrade_status:analyze --all
# Scan specific modules
drush upgrade_status:analyze module1 module2 module3
# Machine-readable output
drush upgrade_status:analyze --all --format=json > d11-report.json
drush upgrade_status:analyze --all --format=codeclimate > d11-report-ci.json
# Scan only custom code
drush upgrade_status:analyze --all --ignore-contrib
# Scan only contrib
drush upgrade_status:analyze --all --ignore-custom
Major Issues (blocking):
REQUEST_TIME constant → Use \Drupal::time()->getRequestTime()user_roles() → Use \Drupal\user\Entity\Role::loadMultiple()file_validate_extensions() → Use file.validator servicesystem_retrieve_file() → No replacement (refactor required)_drupal_flush_css_js() → Use AssetQueryStringInterface::reset()Info.yml Issues:
core_version_requirement to include ^11core_version_requirement: ^9 || ^10 || ^11Example: Inject Time Service
use Drupal\Core\Datetime\TimeInterface;
class MyController extends ControllerBase {
protected $time;
public function __construct(TimeInterface $time) {
$this->time = $time;
}
public static function create(ContainerInterface $container) {
return new static(
$container->get('datetime.time')
);
}
public function myMethod() {
// OLD: $timestamp = REQUEST_TIME;
$timestamp = $this->time->getRequestTime();
}
}
Example: Replace user_roles()
// OLD:
$roles = user_roles(TRUE);
// NEW:
use Drupal\user\Entity\Role;
$roles = Role::loadMultiple();
$role_options = [];
foreach ($roles as $role_id => $role) {
if ($role_id !== 'anonymous') {
$role_options[$role_id] = $role->label();
}
}
# Create patch for contrib module
cd docroot/modules/contrib/module_name
git diff module.info.yml > /path/to/patches/module-d11-info.patch
# Patch content:
--- a/module.info.yml
+++ b/module.info.yml
@@ -2,7 +2,7 @@
name: Module Name
type: module
description: Module description
-core_version_requirement: ^9 || ^10
+core_version_requirement: ^9 || ^10 || ^11
{
"extra": {
"patches": {
"drupal/module_name": {
"Drupal 11 .info.yml support": "patches/module-d11-info.patch"
}
},
"drupal-lenient": {
"allowed-list": [
"drupal/module_name"
]
}
}
}
composer install
drush updb -y
drush cr
# Re-scan to confirm issues resolved
drush upgrade_status:analyze module_name
# Should show "No known issues found"
composer show drupal/module_namecomposer require drupal/module_name:^X.0 --with-all-dependenciesdrush updb -ydrush crdrush upgrade_status:analyze module_name# Error: "Cannot apply patch..."
# 1. Check if module version changed
composer show drupal/module_name
# 2. Search issue queue for updated patch
# Visit drupal.org/node/NODEID (from patch filename)
# 3. Update composer.json with new patch URL
# 4. Or remove patch if merged upstream
# Error: "drupal/module_name requires drupal/core ^9"
# Add to drupal-lenient allowed-list
# Error: "patch ... has already been applied"
# Module maintainer merged the patch - remove from composer.json
# Error during drush updb
# 1. Check error message carefully
# 2. May need to disable module, update, re-enable
drush pm:uninstall module_name
composer require drupal/module_name --with-all-dependencies
drush pm:enable module_name
drush updb -y
--with-all-dependencies for module updatesdrush updb after composer updatespatches/ directoryWhen actively developing a contrib module for drupal.org, use this workflow to avoid constantly updating via composer:
# 1. Set up module repository in temp location
cd /tmp
git clone git@git.drupal.org:project/module_name.git
cd module_name
# Make your changes...
# 2. Remove composer-installed version and symlink your dev copy
cd /path/to/project
rm -rf docroot/modules/contrib/module_name
ln -s /tmp/module_name docroot/modules/contrib/module_name
# 3. Develop and test
# Make changes in /tmp/module_name
# Test immediately in your Drupal site
drush cr # Clear cache as needed
# 4. When ready to publish
cd /tmp/module_name
git add -A
git commit -m "Your changes"
git push origin 1.0.x
# 5. Clean up: remove symlink and reinstall from composer
cd /path/to/project
rm docroot/modules/contrib/module_name
composer install # Reinstalls from drupal.org
Benefits:
Important Notes:
drush crExample: Fixing recurly_commerce_api autoloader issue
# Module needed composer.json autoload section
cd /tmp/recurly_commerce_api
# Edit composer.json to add autoload section
git commit -m "Add PSR-4 autoload configuration"
git push origin 1.0.x
# Back in main project
rm docroot/modules/contrib/recurly_commerce_api
composer install # Gets latest with fix
drush cr
# 1. Find patch in issue queue
# 2. Add to composer.json patches section
# 3. Update module
composer require drupal/module_name:^3.0 --with-all-dependencies
drush updb -y
drush cr
# 4. Test
# 5. Commit
git add composer.json composer.lock patches/
git commit -m "Update module_name to 3.0 with D11 compatibility patch"
# 1. Scan for issues
drush upgrade_status:analyze module_name
# 2. Create info.yml patch if needed
cd docroot/modules/contrib/module_name
# Edit module.info.yml to add ^11
git diff module.info.yml > ../../../patches/module-d11-info.patch
# 3. Add patch to composer.json
# 4. Apply
composer install
drush cr
# 5. Verify
drush upgrade_status:analyze module_name
# 1. Read CHANGELOG/UPDATE.md for breaking changes
# 2. Check issue queue for upgrade path documentation
# 3. Backup database before upgrade
drush sql:dump > backup-before-update.sql
# 4. Update module
composer require drupal/module_name:^3.0 --with-all-dependencies
# 5. Run updates
drush updb -y
# 6. Check for errors
drush watchdog:show --severity=Error --count=20
# 7. Test thoroughly
# 8. If issues, can rollback:
# git checkout composer.json composer.lock
# composer install
# drush sql:cli < backup-before-update.sql