Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Apr 07 2017
Apr 07

After implementing some larger enterprise Drupal 8 websites, I would like to share some insights, how to solve common issues in the deployment workflow with Drupal 8 CMI.

Introduction to Drupal CMI

First of all, you need to understand, how the configuration management in Drupal 8 works. CMI allows you to export all configurations and its dependencies from the database into yml text files. To make sure, you never end up in an inconsistent state, CMI always exports everything. By default, you cannot exclude certain configurations.

Example:

If you change some configuration on the live database, these configurations will be reverted in the next deployment when you use

drush config-import

1

drush config-import

This is helpful and will make sure, you have the same configuration on all your systems.

How can I have different configurations on local / stage / live environments?

Sometimes, you want to have different configurations on your environments. For example, we have installed a “devel” module only on our local environment but we want to have it disabled on the live environment.

This can be achieved by using the configuration split module: https://www.drupal.org/project/config_split

What does Configuration Split?

This module slightly modifies the CMI by implementing a Config Filter (https://www.drupal.org/project/config_filter). Importing and exporting works the same way as before, except some configuration is read from and written to different directories. Importing configuration still removes configuration not present in the files. Thus, the robustness and predictability of the configuration management remains. And the best thing is: You still can use the same drush commands if you have at least Drush 8.1.10 installed.

Configuration Split Example / Installation Guide

Install config_split using composer. You need need at least “8.x-1.0-beta4” and > drush 8.1.10 for this guide.

composer require drupal/config_split "^1.0"

1

composer require drupal/config_split "^1.0"

Enable config_split and navigate to “admin/config/development/configuration/config-split”

drush en config_split -y

1

drush en config_split -y

Optional: Installing the chosen module will make the selection of blacklists / greylists way more easier. You can enable chosen only on admin pages.

composer require drupal/chosen "^1.0"

1

composer require drupal/chosen "^1.0"

I recommend you to create an “environments” subfolder in your config folder. Inside this folder you will have a separate directory for every environment:

Drupal 8 Configuration Management Folders

Now you can configure your environments:

Config Split in Drupal 8 Configuration Management

The most important thing is, that you set every environment to “Inactive”. We will later activate them according to the environment via settings.php

Config Split settings with the Drupal 8 Configuration Management

Here is my example where I enable the devel module on local:

Dev Environment Example

Activate the environments via settings.php

This is the most important part of the whole setup up. Normally, we never commit the settings.php into git. But we have a [environment]-settings.php in git for every environment:

settings.php (not in git) variables-dev.php (in git and included in the settings.php of dev) variables-live.php (in git and included in the settings.php of live) settings.local.php (in git and included locally)

settings.php (not in git)

variables-dev.php (in git and included in the settings.php of dev)

variables-live.php (in git and included in the settings.php of live)

settings.local.php (in git and included locally)

You need to add the following line to the variables-[environment].php. Please change the variable name according to your environment machine name:

// This enables the config_split module $config['config_split.config_split.dev']['status'] = TRUE;

// This enables the config_split module

$config['config_split.config_split.dev']['status'] = TRUE;

If you have done everything correctly and cleared the cache you will see “active (overriden)” in the config_split overview next to the current environment.

Now you can continue using

drush config-import -y drush config-export -y

drush config-import -y

drush config-export -y

and config_split will do the magic.

How can I exclude certain Config Files and prevent them to be overridden / deleted on my live environment?

The most prominent candidates for this workflow are webforms and contact forms. In Drupal 7, webforms are nodes and you were able to give your CMS administrator the opportunity to create their own forms.

In Drupal 8 webforms are config entities, which means that they will be deleted while deploying if the yml files are not in git.

After testing a lot of different modules / drush scripts, I finally came up with an easy to use workflow to solve this issue and give CMS administrators the possibility to create webforms without git knowledge:

Set up an “Excluded” environment

First of all, we need an “excluded” environment. I created a subfolder in my config-folder and added a .htaccess file to protect the content. You can copy the .htaccess from an existing environment, if you are lazy. Don’t forget to deploy this folder to your live system before you do the next steps.

Folders

Excluded

Now you can exclude some config files to be excluded / grey-listed on your live environment:

webform.webform.* contact.form.*

webform.webform.*

contact.form.*

Greylist Webform in Config Split

Set the excluded environment to “Inactive”. We will later enable it on the live / dev environment via settings.php.

Enable “excluded” environment and adapt deployment workflow

We enable the “excluded” environment on the live system via variables-live.php (see above):

// This will allow module config per environment and exclude webforms from being overridden $config['config_split.config_split.excluded']['status'] = TRUE;

// This will allow module config per environment and exclude webforms from being overridden

$config['config_split.config_split.excluded']['status'] = TRUE;

In your deployment workflow / script you need to add the following line before you do a drush config-import:

#execute some drush commands echo "-----------------------------------------------------------" echo "Exporting excluded config" drush @live config-split-export -y excluded echo "-----------------------------------------------------------" echo "Importing configuration" drush @live config-import -y

1

2

3

4

5

6

7

8

#execute some drush commands

echo "-----------------------------------------------------------"

echo "Exporting excluded config"

drush @live config-split-export -y excluded

echo "-----------------------------------------------------------"

echo "Importing configuration"

drush @live config-import -y

The drush command “drush @live config-split-export -y excluded” will export all webforms and contact forms created by your CMS administrators into the folder “excluded”. The “drush config-import” command will therefore not delete them and your administrators can happily create their custom forms.

Benefit of disable “excluded” on local environment

We usually disable the “excluded” environment on our local environment. This allows us to create complex webforms on our local machine for our clients and deploy them as usual. In the end you can have a mix of customer created webforms and your own webforms which is quite helpful.

Final note

The CMI is a great tool and I would like to thank the maintainers of the config_split module for their great extension. This is a huge step forward making Drupal 8 a real Enterprise CMS Tool.

If you have any questions, don’t hesitate to post a comment.

Jan 05 2017
Jan 05

As a follow-up to my previous blog post about the usage of Migrate API in Drupal 8, I would like to give an example, how to import multilingual content and translations in Drupal 8.

Prepare and enable translation for your content type

Before you can start, you need to install the “Language” and “Content Translation” Module. Then head over to “admin/config/regional/content-language” and enable Entity Translation for the node type or the taxonomy you want to be able to translate.

As a starting point for setting up the migrate module, I recommend you my blog post mentioned above. To import data from a CVS file, you also need to install the migrate_source_csv module.

Prerequisites for migrating multilingual entities

Before you start, please check the requirements. You need at least Drupal 8.2 to import multilingual content. We need the destination option “translations”, which was added in a patch in Drupal 8.2. See the corresponding drupal.org issue here.

Example: Import multilingual taxonomy terms

Let’s do a simple example with taxonomy terms. First, create a vocabulary called “Event Types” (machine name: event_type).

Here is a simplified dataset:

Id Name Name_en 1 Kurs Course 2 Turnier Tournament

You may save this a csv file.

Id;Name;Name_en 1;Kurs;Course 2;Turnier;Tournament

Id;Name;Name_en

1;Kurs;Course

2;Turnier;Tournament

The recipe to import multilingual content

As you can see in the example data,  it contains the base language (“German”) and also the translations (“English”) in the same file.

But here comes a word of warning:

Don’t try to import the term and its translation in one migration run. I am aware, that there are some workarounds with post import events, but these are hacks and you will run into troubles later.

The correct way of importing multilingual content, is to

  1. create a migration for the base language and import the terms / nodes. This will create the entities and its fields.
  2. Then, with an additional dependent migration for each translated language, you can then add the translations for the fields you want.

In short: You need a base migration and a migration for every language. Let’s try this out.

Taxonomy term base language config file

In my example, the base language is “German”. Therefore, we first create a migration configuration file for the base language:

This is a basic example in migrating a taxonomy term in my base language ‘de’.

Put the file into <yourmodule>/config/install/migrate.migration.event_type.yml and import the configuration using the drush commands explained in my previous blog post about Migration API.

id: event_type label: Event Types source: plugin: csv # Full path to the file. Is overriden in my plugin path: public://csv/data.csv # The number of rows at the beginning which are not data. header_row_count: 1 # These are the field names from the source file representing the key # uniquely identifying each node - they will be stored in the migration # map table as columns sourceid1, sourceid2, and sourceid3. keys: - Id ids: id: type: string destination: plugin: entity:taxonomy_term process: vid: plugin: default_value default_value: event_type name: source: Name language: 'de' langcode: plugin: default_value default_value: 'de' #Absolutely necessary if you don't want an error migration_dependencies: {}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

id: event_type

label: Event Types

source:

  plugin: csv

  # Full path to the file. Is overriden in my plugin

  path: public://csv/data.csv

  # The number of rows at the beginning which are not data.

  header_row_count: 1

  # These are the field names from the source file representing the key

  # uniquely identifying each node - they will be stored in the migration

  # map table as columns sourceid1, sourceid2, and sourceid3.

  keys:

    - Id

ids:

  id:

    type: string

destination:

  plugin: entity:taxonomy_term

process:

  vid:

   plugin: default_value

   default_value: event_type

  name:

    source: Name

    language: 'de'

  langcode:

    plugin: default_value

    default_value: 'de'

#Absolutely necessary if you don't want an error

migration_dependencies: {}

Taxonomy term translation migration configuration file:

This is the example file for the English translation of the name field of the term.

Put the file into <yourmodule>/config/install/migrate.migration.event_type_en.yml and import the configuration using the drush commands explained in my previous blog post about Migration API.

id: event_type_en label: Event Types english source: plugin: csv # Full path to the file. Is overriden in my plugin path: public://csv/data.csv # The number of rows at the beginning which are not data. header_row_count: 1 keys: - Id ids: id: type: string destination: plugin: entity:taxonomy_term translations: true process: vid: plugin: default_value default_value: event_type tid: plugin: migration source: id migration: event_type name: source: Name_en language: 'en' langcode: plugin: default_value default_value: 'en' #Absolutely necessary if you don't want an error migration_dependencies: required: - event_type

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

id: event_type_en

label: Event Types english

source:

  plugin: csv

  # Full path to the file. Is overriden in my plugin

  path: public://csv/data.csv

  # The number of rows at the beginning which are not data.

  header_row_count: 1

  keys:

    - Id

ids:

  id:

    type: string

destination:

  plugin: entity:taxonomy_term

  translations: true

process:

  vid:

    plugin: default_value

    default_value: event_type

  tid:

    plugin: migration

    source: id

    migration: event_type

  name:

    source: Name_en

    language: 'en'

  langcode:

     plugin: default_value

     default_value: 'en'

#Absolutely necessary if you don't want an error

migration_dependencies:

  required:

    - event_type

Explanation and sum up of the learnings

The key in the migrate configuration to import multilingual content are the following lines:

destination: plugin: entity:taxonomy_term translations: true

destination:

  plugin: entity:taxonomy_term

  translations: true

These configuration lines instruct the migrate module, that a translation should be created.

tid: plugin: migration source: id migration: event_type

tid:

  plugin: migration

  source: id

  migration: event_type

This is the real secret. Using the process plugin migration,  we maintain the relationship between the node and its translation.The wiring via the tid field make sure, that Migrate API will not create a new term with a new term id. Instead, the existing term will be loaded and the translation of the migrated field will be added. And thats exactly what we need!

Now go ahead and try to create a working example based on my explanation. Happy Drupal migrations!

May 04 2016
May 04

We at Liip AG believe, that the migration API is the best and most efficient way to import data into Drupal. Here are some reasons, why you should use migrate instead of the feeds module or any other custom importer modules:

  • Since Drupal 8, Migrate API is part of Drupal core
  • Migrate will be maintained and supported as long as Drupal 8 exists as it provides the upgrade path for older Drupal versions to Drupal 8
  • Migrate is sponsored by Acquia and mainly supported by Mike Ryan, a well-known and skilled Drupal developer.
  • Migrate has out of the box support for all important Drupal objects such as nodes, users, taxonomy terms, users, files, entities and comments.
  • Migrate has a Drush integration, that allows you, to run import tasks from command-line or via cron job
  • Migrate maintains a mapping-table, has rollback functionality and even supporting a highwater field, that allows to import only new or changed datasets.
  • Migrate is well documented and there is an example module.

Getting started with Drupal 8 Migrate Module

The Migrate 8 module in core is only an API. There is no user interface. This makes it difficult for new developer to start with Migrate 8.

I suggest you, to install the below listed extension modules right away before you start developing if you want to realize the full potential of migrate:

Migrate Plus (https://www.drupal.org/project/migrate_plus)

  • Extends the migration framework with groups
  • Delivers a well documented example module

Migrate Tools (https://www.drupal.org/project/migrate_tools)

  • Provides Drush commands for running and managing migrations in Drupal 8

Migration Source Plugins

Installing a fully working Drupal 8 Migrate setup using composer

Instead of starting now to download in install all the module mentioned above, you can use my installation profile based on a composer.json file. Because a lot of modules with specific version are involved, I have prepared a fully working migrate example environment for a Liip hackday.

If you want to quickly start with the migrate module, head over to my github repository and install Drupal 8 Migrate using composer and drush. You just need to follow the instruction in the README.md

https://github.com/ayalon/drupal8-migrate

Comparing Drupal Migrate 7 with Drupal Migrate 8

Some of you might already have used Migrate 7. A traditional mapping was done in the constructor of a Migration class:

public function __construct($arguments) { parent::__construct($arguments); $this->description = t('Page Placeholder import'); // Set up our destination - nodes of type migrate_example_beer. $this->destination = new MigrateDestinationNode('page'); $this->csvFile = DRUPAL_ROOT . '/docs/navigation.csv'; $this->map = new MigrateSQLMap($this->machineName, array( 'ID' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), ), MigrateDestinationNode::getKeySchema() ); $this->source = new MigrateSourceCSV($this->csvFile, array(), array('header_rows' => 1, 'delimiter' => ';')); // Force update. $this->highwaterField = array(); // Mapped fields. $this->addFieldMapping('title', 'name'); $this->addFieldMapping('uid')->defaultValue(1); $this->addFieldMapping('status')->defaultValue(1); $this->addFieldMapping('promote')->defaultValue(0); $this->addFieldMapping('sticky')->defaultValue(0); $this->addFieldMapping('language')->defaultValue('de'); // Unmapped destination fields. $this->addUnmigratedDestinations(array( 'body:format', 'changed', 'comment', 'created', 'is_new', 'log', 'revision', 'revision_uid', 'tnid', 'translate', )); }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

public function __construct($arguments) {

  parent::__construct($arguments);

  $this->description = t('Page Placeholder import');

  // Set up our destination - nodes of type migrate_example_beer.

  $this->destination = new MigrateDestinationNode('page');

  $this->csvFile = DRUPAL_ROOT . '/docs/navigation.csv';

  $this->map = new MigrateSQLMap($this->machineName,

    array(

      'ID' => array(

        'type' => 'int',

        'unsigned' => TRUE,

        'not null' => TRUE,

      ),

    ),

    MigrateDestinationNode::getKeySchema()

  );

  $this->source = new MigrateSourceCSV($this->csvFile, array(), array('header_rows' => 1, 'delimiter' => ';'));

  // Force update.

  $this->highwaterField = array();

  // Mapped fields.

  $this->addFieldMapping('title', 'name');

  $this->addFieldMapping('uid')->defaultValue(1);

  $this->addFieldMapping('status')->defaultValue(1);

  $this->addFieldMapping('promote')->defaultValue(0);

  $this->addFieldMapping('sticky')->defaultValue(0);

  $this->addFieldMapping('language')->defaultValue('de');

  // Unmapped destination fields.

  $this->addUnmigratedDestinations(array(

    'body:format',

    'changed',

    'comment',

    'created',

    'is_new',

    'log',

    'revision',

    'revision_uid',

    'tnid',

    'translate',

  ));

}

In Migrate 8 this format has been replaced with yaml files. The same mapping as above looks like that in Drupal 8:

# Migration configuration id: page_node label: Dummy pages migration_group: liip source: plugin: page_node # Full path to the file. Is overridden in my plugin path: public://csv/navigation_small.csv # The number of rows at the beginning which are not data. header_row_count: 1 # These are the field names from the source file representing the key # uniquely identifying each node - they will be stored in the migration # map table as columns sourceid1, sourceid2, and sourceid3. keys: - ID destination: plugin: entity:node process: type: plugin: default_value default_value: page title: name uid: plugin: default_value default_value: 1 sticky: plugin: default_value default_value: 0 status: plugin: default_value default_value: 1 promote: plugin: default_value default_value: 0 'body/value': body 'body/summary': excerpt #Absolutely necessary if you don't want an error migration_dependencies: {}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

# Migration configuration

id: page_node

label: Dummy pages

migration_group: liip

source:

plugin: page_node

# Full path to the file. Is overridden in my plugin

path: public://csv/navigation_small.csv

# The number of rows at the beginning which are not data.

header_row_count: 1

# These are the field names from the source file representing the key

# uniquely identifying each node - they will be stored in the migration

# map table as columns sourceid1, sourceid2, and sourceid3.

keys:

   - ID

destination:

plugin: entity:node

process:

type:

   plugin: default_value

   default_value: page

title: name

uid:

   plugin: default_value

   default_value: 1

sticky:

   plugin: default_value

   default_value: 0

status:

   plugin: default_value

   default_value: 1

promote:

   plugin: default_value

   default_value: 0

'body/value': body

'body/summary': excerpt

#Absolutely necessary if you don't want an error

migration_dependencies: {}

Understanding the new mapping with yaml files in Migrate 8

The mapping is quite straightforward.

  • First you have to define your Migrate source. In the example we have used a CSV source plugin. (https://www.drupal.org/node/2129649)
  • Then you can map the source fields to a Migrate destination. In our case, we use a node destination (https://www.drupal.org/node/2174881)
  • You can map now all source fields to destination fields, for example you map a column of the CSV file to the node title field
  • Every field can be processed and modified before it is passed to the final node field. There are a lot of useful process plugins like “default_value”, “callback” or “skip_if_empty”
  • You can find a list of all process plugins here: https://www.drupal.org/node/2129651
  • Of course you can easily write your own plugins and use them while migrating data

Example: Importing a menu tree and create dummy nodes using Drupal Migrate 8

For demonstration purpose I created a small module, that reads a menu tree from a CSV file and imports into Drupal.

The module is split into 2 tasks:

  1. Creating a page node for every row in the csv file
  2. Create a menu item for every row and attach it to the correct node

Migrate handles these dependencies in the yaml file:

config/install/migrate_plus.migration.menu_item.yml

1

config/install/migrate_plus.migration.menu_item.yml

Dependancies

migration_dependencies: required: - page_node

migration_dependencies:

required:

   - page_node

See the full module code here:

https://github.com/ayalon/drupal8-migrate/tree/master/web/modules/custom/migrate_menu

Developing your own Drupal 8 migrate modules and fighting caching issues

You have learned, that the whole migration mapping is now done in yaml files. But how about writing your own migration yaml files?

Unfortunately, there are some pitfalls for new Drupal 8 developers. Because of the Configuration Management Interface (https://www.drupal.org/documentation/administer/config) of Drupal 8, all yml files in the “config/install” directory are only imported when installing the module.

This is very impractical if you want to develop new configuration files. To address this, a module “Configuration Development” (https://www.drupal.org/project/config_devel) which resolves the caching issues can be installed. It is possible to import certain yml files on every request. But unfortunately drush commands are not supported yet. So we need to add all yaml files we want to import into a new section in our module.info.yml.

config_devel: install: - migrate_plus.migration.page_node - migrate_plus.migration.menu_item - migrate_plus.migration_group.liip

config_devel:

install:

   - migrate_plus.migration.page_node

   - migrate_plus.migration.menu_item

   - migrate_plus.migration_group.liip

Then we can run the following commands after updating the yml file. This will import the new configuration file into CMI.

drush cdi <module_name> drush cr

drush cdi <module_name>

drush cr

In short:

You always have to remember, that you have to import the yaml files and clear the cache after changing the mapping before executing the migration again.

Testing and running your migration

If your module is set up correctly, you can run “drush ms” to see if the migration is ready:

drush-1

Now you can run the migration using

drush mi <migration_name>

1

drush mi <migration_name>

drush-2

If you want to update already imported items you can use the –update option:

drush-3

Advanced example importing a JSON source into Drupal nodes

During the last hackday at Liip I wrote a small module that is consuming a JSON source from

http://jsonplaceholder.typicode.com importing some postings and comments to a Drupal 8 website.

The module is split into 3 tasks:

  1. A feature module that installs the node type and fields called “migrate_posts”
  2. A migration importing post nodes
  3. A migration importing comments and attaching them to the already imported nodes

You can find the feature module here:

https://github.com/ayalon/drupal8-migrate/tree/master/web/modules/custom/migrate_posts

The migration module itself is here:

https://github.com/ayalon/drupal8-migrate/tree/master/web/modules/custom/migrate_json_hackday

Migration Process Plugin

Inside the migration module from above you will find a simple process plugin. The subject field of a comment in drupal only accepts a certain number of chars by default. Therefore I wrote a small process plugin, that truncates the incoming subject string:

subject: plugin: truncate source: name

subject:

plugin: truncate

source: name

The process plugin needs an annotation (https://api.drupal.org/api/drupal/core%21core.api.php/group/annotation/8.2.x) to be picked up by the migration API. You can later refer to the id in the yaml file:

<?php namespace Drupal\migrate_json_hackday\Plugin\migrate\process; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\Row; use Drupal\Component\Utility\Unicode; /** * * @MigrateProcessPlugin( * id = "truncate" * ) */ class Truncate extends ProcessPluginBase { /** * {@inheritdoc} */ public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { return Unicode::truncate($value, 64); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<?php

namespace Drupal\migrate_json_hackday\Plugin\migrate\process;

use Drupal\migrate\MigrateExecutableInterface;

use Drupal\migrate\ProcessPluginBase;

use Drupal\migrate\Row;

use Drupal\Component\Utility\Unicode;

/**

*

* @MigrateProcessPlugin(

*   id = "truncate"

* )

*/

class Truncate extends ProcessPluginBase {

  /**

   * {@inheritdoc}

   */

  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {

    return Unicode::truncate($value, 64);

  }

}

You can see the whole module code under:

https://github.com/ayalon/drupal8-migrate/tree/master/web/modules/custom/migrate_json_hackday

Final Words

At the moment, there migration is under heavy development. For Drupal 8.1 a lot of changes have been made and these changes were breaking a lot of helper modules. Especially the Migrate UI is not working since several months now under Drupal 8.1. You can see more information under https://www.drupal.org/node/2677198.

But nevertheless, Migrate 8 is getting more and more mature and stable. And it’s time to learn the new yaml syntax now! If you have any suggestions, just drop a comment.

Oct 08 2015
Oct 08

11 Liipers attended the DrupalCon Barcelona in September 2015. We learned a lot about Drupal 8. And heard over and over again: “Start working with Drupal 8 now!”.

But let’s have a look back: The development of Drupal 8 took more than 4 years and for me as a developer it sometimes seems, like no stone was left unturned. Almost everything has changed. Symfony2 walked onto the stage and brought some paradigm shifts in Drupal 8 that may prove to be a challenge for us as developers.

But the good news is that there are so much new features and tools, that make developing enterprise websites so much easier, that I would like to start new projects with Drupal 8 exclusively and never use Drupal 7 again ;-)

At DrupalCon, there were a lot of interesting sessions about exciting features for developers. My personal favourites were the following:

But what about new projects with Drupal 8?

Sure, all these new features sound exciting. But what about building new customer projects with Drupal 8? The answer isn’t that easy.

We at Liip already started to work with Drupal 8 and we can share some experience with other companies out there, who would like to start working with Drupal 8.

New simple CMS project with Drupal 8 will work out of the box!

Simple CMS projects that rely on Drupal 8 Core without the help of too many contrib modules will work out of the box. So many great features like Views, Page Manager and CKEditor are now part of the core. Drupal 8 matured and is now a full featured enterprise content management system capable of building websites based on structured data.

Nevertheless, there is an initial investment you will have to do, because the learning curve of Drupal 8 is quite steep. If you are not familiar with Object Oriented Programming (OOP) and Symfony2, you will have to put some effort to learn the new concepts. But as soon you managed that, your Drupal installations will become easier to deploy and ways better maintainable.

In our first smaller projects, we calculate with a 10-20% additional costs, because of missing knowledge and experience. we also will have to fix some of these early adopter bugs. These costs will be covered mainly by my personal education budget and also by some extra budget Liip AG has reserved for Drupal 8 transition phase.

If you plan to start a new project you should try to go with Drupal 8 if it’s somehow possible. This will protect your investment for the next few years and make sure, that the long term costs (aka maintenance) stay low. You can expect that in the near future all new Drupal websites will be build with Drupal 8. Additionally you can profit from all the new features. Even as an end user you have a much better user experience and a lot of benefits. If you are uncertain, you may contact us. We have a lot of expertise in this field and will figure out together with you if your project will work out with Drupal 8. In the end, it will be up to you to decide which version is implemented.

For new and complex project you will have to calculate quite some extra efforts!

Bigger project often depend on a lot of custom module. For example complex media management (as we had it in Drupal 7) is not ready at all. For every contrib module you have to evaluate and test, if there is a working Drupal 8 version. If there is none, you will probably have to upgrade parts of the module or looking for an alternative.

We calculate with more than 50% additional costs, if we have to upgrade complex contrib modules or parts of it. As we would like to contribute these modules back to the community, this number can grow quickly if you try to implement the new module in a generic reusable way.

Upgrading existing big Drupal 7 project To Drupal 8: Stay away!

At the time of writing I consider it absolutely senseless to upgrade big projects to Drupal 8 that rely on a lot of contrib modules. You would have to upgrade a lot of contrib modules and also completely rewrite your custom modules. Updating a contrib module means, that you have to understand the code someone else has written and transform it into working module on Drupal 8 (where you probably are not as experienced) and in the same time discover and use the new system / APIs / services. Quite a brave mission that probably will end up in a mess. You should try to avoid that.

So what should I do now?

Every Drupal site is different and especially the customer needs are different and have to be considered on a case by case basis. You will certainly have to calculate with additional internal costs that you cannot bill to your customer in your first Drupal 8 project.

But you should definitely now start working and building websites with Drupal 8. You will enjoy all the new features and make your customer happy with a fast, testable and user friendly system. And we hope, that you also contribute and help upgrading key modules. This helps the Drupal community a lot and will enable all of us to do more complex Drupal 8 projects in the near future.

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web