Managing Your Drupal Project with Composer

Published onMonday 10, October 2016

Drupal Commerce was started without writing any Drupal code. Our libraries set Drupal Commerce off the island before Drupal was able to support using third party library not provided by core.

Drupal now ships without third party libraries committed, fully using Composer for managing outside dependencies. However, that does not mean the community and core developers have everything figured out, quite yet.

YNACP: Yet Another Composer Post. Yes. Because as a co-maintainer of Drupal Commerce we're experiencing quite a lot of issue queue frustration. I also want to make the case of "let's make life eaiser" for working with Drupal. As you read compare the manual sans-Composer process for local development and remote deployment versus the Composer flows.

Before we begin

We're going to be discussing Composer. There's specific terminologies I'll cover first.

  • composer.json1: defines metadata about the project and dependencies for the project.
  • composer.lock2: metadata file containing computed information about dependencies and expected install state.
  • composer install3: downloads and installs dependencies, also builds the class autoloader. If a .lock file is available it will install based off of the metadata. Otherwise it will calculated and resolve the download information for dependencies.
  • composer update4: updates defined dependencies and rebuilds the lock file.
  • composer require5: adds a new dependency, updates the JSON and .lock file.
  • composer remove6: removes a dependency, updates the JSON and .lock file.

All Composer commands need to run in the same directory as your composer.json file.

Installing Drupal

There are multiple ways to install Drupal. This article focuses on working with Composer, for general installation help review the official documentation at https://www.drupal.org/docs/8/install

Install from packaged archive

Drupal.org has a packaging system which provides zip and tar archives. These archives come with all third party dependencies downloaded.

You download the archive, extract the contents and have an installable Drupal instance. The extracted contents will contain the vendor directory and a composer.lock file.

Install via Composer template

A community initiative was started to provide a Composer optimized project installation for Drupal. The Drupal Composer project provided a version of Drupal core which could be installed via Composer and a mirror of Drupal.org projects via a Composer endpoint (This has been deprecated in favor of the Drupal.org endpoint).

To get started you run the create-project7 command. 

composer create-project drupal-composer/drupal-project:8.x-dev some-dir --stability dev --no-interaction

This will create some-dir folder which holds the vendor directory and a web root directory (Drupal.) This will allow you to install Drupal within a subdirectory of the project, which is a common application structure.

This also keeps your third party libraries out of access from your web server.

Review the repository for documentation on how to use the project, including adding and updating core/projects: https://github.com/drupal-composer/drupal-project.

Adding dependencies to Drupal

Without Composer

Modules, themes, and profiles are added to Drupal my placing them in a specific directory. This can be done by visiting Drupal.org, downloading the packaged archive and extracting it to the proper location.

There's a problem with this process: it's manual and does not ensure any of the project's dependencies were downloaded. Luckily Composer is a package and dependency manager!

With Composer

To add a dependency we use the composer require command. This will mark the dependency, download any of its own. 

Note if you did not use project base: Currently there is no out of the box way to add Drupal.org projects to a standard Drupal installation. You will need to run a command to the endpoint.

composer config repositories.drupal composer https://packages.drupal.org/8

Let's use the Panels module as an example. Running the following command would add it to your Drupal project.

composer require drupal/panels

This will install the latest stable version of the Panels version. If you inspect your composer.json file you should see something like the following

"require": {
  "drupal/panels": "3.0-beta4",
}

One of the key components is the version specification. This tells Composer what version it can install, and how it can update.

  • 3.0 will be considered a specific version and never update.
  • ~3.0 will consider any patch version as a possible installation option, such as new betas, RCs.
  • ~3 will allow any minor releases to be considered for install or update.
  • ^3.0 will match anything under the major release — allowing any minor or patch release.

You can specify version constraints when adding a dependency as well. This way you can define of you will allow minor or patch updates when updating.

composer require drupal/panels:~3.0

This will allow versions 3.0-beta5,3.0-rc1, 3.0 to be valid update versions.

Know what! The same versioning patterns exist in NPM and other package managers.

Updating dependencies

Without Composer

As stated with installing dependencies, it could be done manually. But this requires knowing if any additional dependencies need to be updated. In fact, this is becoming a common issue in the Drupal.org issue queues.

With Composer

Again, this is where Composer is utilized and simplifies package management.

Going from our previous example, let's say that Panels has a new patch release. We want to update it. We would run

composer update drupal/panels --with-dependencies

This will update our Drupal project and any of its dependencies. Why is this important? What if Panels required the newest version of Entity Reference Revisions for a critical fix? Without a package manager, we would have not known or possibly updated.

Why we need --with-dependencies

When Composer updates a dependency, it does not automatically update its dependencies. Why? No idea, apparently the maintainers do not believe it should.

Updating Drupal core

Without the Composer template, documented method

If you installed Drupal through the normal process, via an extracted archive, you have to manually update in the same fashion. You will need to remove all files provided by Drupal core — *including your possibly modified composer.json file*.

Rightly so, you can move your modified .htaccess, composer.json, or robots.txt and move them back. However, you’ll need to make sure your composer.json matches the current Drupal core’s requirements and run composer update.

That’s difficult.

The official documentation: https://www.drupal.org/docs/7/updating-your-drupal-site/update-procedur…
 

UPDATE: Without the Composer template, still using just Composer.

Bojan alerted me that there is a way to update Drupal core without the Composer template using just composer. It requires a minor modification of the composer.json file that is shipped with Drupal core. You just need to move the drupal/core definition out of replace and into require.

The end result should look like the following

    "require": {
        "composer/installers": "^1.0.21",
        "wikimedia/composer-merge-plugin": "~1.3",
        "drupal/core": "~8.2"
    },
    "replace": { },

You can then run composer update drupal/core --with-dependencies and have an up to date Drupal. However, none of the root files (index.php, etc) will be modified.

Updating Drupal core via the Composer template

If you have setup Drupal with the Composer template or any Composer based workflow, all you need to do is run the following command (assuming you’ve tagged the drupal/core dependency as ^8.x.x or ~8, ~8.1, ~8.2)

composer update drupal/core --with-dependencies

This will update Drupal core and its files alongside the drupal-composer/drupal-scaffold project.

Using patches with Composer

I have been a fan of using build tools with Drupal, specifically  using Drush and makefiles to manage Drupal platforms. However, when I first used Composer I was concerned on how to use patches or pull requests not yet merged with the project — without maintaining some kind of fork.

Cameron Eagans create the composer-patches  project. This will apply patches to your dependencies. The project’s README fully documents its use, so I’ll cover it quickly here.

Patches are stored in a patches portion of the extra schema of the JSON file.

  "extra": {
    "patches": {
      "drupal/commerce”: {
        "#2805625: Add a new service to manage the product variation rendering": "https://www.drupal.org/files/issues/add_a_new_service_to-2805625-4.patch"
      }
    }
  }

This patches Drupal Commerce with a specific patch. 
   

Using GitHub PRs as a patch

Patches are great, as they let you use uncommitted functionality immediately. A problem can arise when you need code from a GitHub pull request (or so it seems.) For instance, Drupal Commerce is developed on GitHub since DrupalCI doesn’t support Composer and contributed projects yet.

Luckily we can take the PR for the issue used in the example https://github.com/drupalcommerce/commerce/pull/511 and add .patch to it to retrieve a patch file: https://github.com/drupalcommerce/commerce/pull/511.patch

We could then update our composer.json to use the pull request’s patch URL and always have up to date versions o the patch.

  "extra": {
    "patches": {
      "drupal/commerce”: {
        "#2805625: Add a new service to manage the product variation rendering": "https://github.com/drupalcommerce/commerce/pull/511.patch"
      }
    }
  }