Drupal Composer recipes

Drupal Composer recipes

If you still don't use Composer for managing Drupal projects, you should start doing this right now! The Drupal Composer template will help you to set things up. It's really easy to setup a new project.

If you’re still not convinced, check out the benefits of the Drupal Composer workflow:

  • No need to store contrib code (and the core!) in your version control system.
  • A single package management tool for everything: Drupal core, contrib modules, JS libraries, your own shared modules, etc.
  • Patching of core and modules is easier than ever.
  • It's way simpler than git submodules.

(All recipes consider Drupal 8, but they should work for Drupal 7 as well)

Installing contrib modules

  • composer require drupal/<MODULE_NAME>:~8.0 to get the latest stable version (or latest dev, if there is no “stable release")
  • composer require drupal/<MODULE_NAME>:dev-<BRANCH_NAME> to get the latest dev version
  • composer require drupal/<MODULE_NAME>:dev-<BRANCH_NAME>#<COMMIT_HASH> to get the exact version

Updating Drupal core/modules

  • composer update to update everything
  • composer update --dry-run to check for updates
  • composer update drupal/<MODULE_NAME> to update a single module

Patching packages

The cweagans/composer-patches plugin (which comes with the Drupal Composer template) will take patches from the "extra" section of the composer.json file:

   "extra": {
       "patches": {
           "<PACKAGE/NAME>": {
               "<PATCH DESCRIPTION>": "<PATH/TO/PATCH/OR/URL>",
               ...
           },
           ...
       }
   }

Example:

   "extra": {
       "patches": {
           "drupal/core": {
               "Fix language detection": "patches/2189267-24.patch"
           }
       }
   }

After a new patch is added run:

  • composer install to apply patch
  • composer update nothing (or composer update --lock) to make the composer-patches plugin write necessary changes to the composer.lock file

Installing custom/forked modules from Github

For the case when a module repository contains its own composer.json

Register the repository in the "repositories" section of the composer.json file:

   "repositories": [
       {
           "type": "vcs",
           "url": "https://github.com/<REPOSITORY/NAME>"
       },
       ...
   ],

Use composer require drupal/<MODULE_NAME>:dev-<BRANCH_NAME>#<COMMIT_HASH> to install the module.

For the case when the composer.json file is missing from the module repository

You'll need to use a bit more verbose variant:

   "repositories": [
       {
           "type": "package",
           "package": {
               "name": "drupal/<MODULE_NAME>",
               "version": "dev-custom",
               "type": "drupal-module",
               "source": {
                   "type": "git",
                   "url": "git@github.com:<REPOSITORY/NAME>.git",
                   "reference": "<BRANCH-NAME>"
               }
           }
       },
       ...
   ],

Use composer require drupal/<MODULE_NAME>:dev-custom#<COMMIT_HASH> to install the module.

For the case when the destination path should be different than modules/contrib

In addition to the above recipes, use the composer/installers plugin:

   "extra": {
       "installer-paths": {
           "web/modules/custom/<MODULE_NAME>": ["drupal/<MODULE_NAME>"],
           ...
       }
   }

Adding a JS library

Most popular libraries can be added easily with composer as they exist on Packagist. The tricky part is that most Drupal modules require that libraries are saved under the "libraries" directory while Composer installs them to "vendor". The composer/installers plugin can override package paths, but only for packages that depend on it. So, you'll need to override the composer.json file of the library stating that it has the composer/installers dependency.

Let's take a look at an example:

   "repositories": [
       {
           "type": "package",
           "package": {
               "name": "enyo/dropzone",
               "version": "4.3",
               "type": "drupal-library",
               "source": {
                   "url": "https://github.com/enyo/dropzone.git",
                   "type": "git",
                   "reference": "master"
               },
               "dist": {
                   "url": "https://github.com/enyo/dropzone/archive/v4.3.0.zip",
                   "type": "zip"
               },
               "require": {
                   "composer/installers": "~1.0"
               }
           }
       },
       ...
   ],  
   ...   
   "extra": {
       "installer-paths": {
           "web/libraries/{$name}" : ["type:drupal-library"],
           ...
       }
   }

After the above is added to the composer.json, run composer require enyo/dropzone:4.3 to download the library. Also, here we have used the exact version and added the "dist" section to make it possible for Composer to download a zip archive instead of cloning the Git repository.

Switch a dependency package to a forked version

Add the forked repository to the composer.json:

   "repositories": [
       {
           "type": "vcs",
           "url": "https://github.com/<REPOSITORY/NAME>"
       },
       ...
   ],

Run composer require <PACKAGE/NAME>:dev-<BRANCH_NAME>#<COMMIT_HASH>

Update an existing Drupal 8 project to use Composer

  • Make a backup ;)
  • Delete everything that will be managed by Composer: Drupal's "core" folder, contrib modules, etc.
  • Delete all Drupal "root" files, such as index.php, update.php, README.txt... All of them.
  • Create "web" directory in the project root and move all remaining Drupal folders (sites, modules, themes, libraries, profiles, etc.) into it.
  • Copy the Drupal Composer template files to the project root.
  • Prepare a list of Drupal core and contrib module versions (and everything else that will be managed by Composer) that are used currently on the project. Then run composer require specifying every dependency with the exact version. You'll need to convert Drupal versions into Composer versions, here are some examples:
    • drupal/core:8.1.8 is obvious
    • drupal/admin_toolbar:8.1.15 refers to admin_toolbar 8.x-1.15
    • drupal/ctools:8.3.0-alpha26 refers to ctools 8.x-3.0-alpha26
    • drupal/config_installer:dev-8.x-1.x#a16cc9acf84dd12b9714def53be0ce280a5b0c1a refers to config_installer dev snapshot made from the a16cc9a commit of the 8.x-1.x branch
  • Manually update versions of Drupal core and contrib modules to "~8.0" in the "require" section of the composer.json file. That will make updates possible.
  • Run composer drupal-scaffold which will create the Drupal "root" files.
  • Make your webserver use "web" directory as the web root.