Building our first website with Drupal 8
Due to the 10th anniversary of our company, we have been busy redefining our strategy and mission. Our way of working with our clients has evolved over the years, and our current website no longer reflected the way we currently do projects. Where our previous website just showed a large list of our clients and projects, we no longer want to focus solely on the end-product, but rather on the process of getting to this end-product. It was time for an overhaul of our company’s website.
One thing that has remained constant throughout the years is our love for Drupal. We have been using it for years, and by developing most of our projects on this framework, we were able to rely on internal (and external) standards, enabling fast development of the basics of each project and focus on the design and interaction, and other more project-specific features.
Drupal 7 has been our go-to framework for years, and has served both ourselves and our clients rather well over the years. Even though a basic install of Drupal 7 was sufficient for smaller projects, most of our projects needed quite a lot of third-party (contribution) modules, even so much that many modules could have considered to be a necessary part of Drupal core. Examples of these are i18n (internationalisation), views, pathauto (automatic URL aliases), entity API, file entity and media. The quality of these modules was en par with the Drupal standards, and installing and using them was usually an easy process.
Drupal 8 had been announced quite some time ago, in fact, plans for it were already made at the launch of Drupal 7, which is pretty much standard practice for Drupal. The plans were big, possibly the biggest rewrite in Drupal history. During the lifetime of Drupal 7, a lot of features were added with contrib modules that did not make the core release. Probably the best example is the Entity API module, an extension on the core’s entity API with the goal of standardising a unified way of dealing with entities and their properties. Quite a lot of contrib modules make use of this entity API, de facto making it a requirement for most projects. Drupal 8 was to incorporate this unified way of handling entities in core. Another big gap in Drupal 7 core was i18n, which now has been completely incorporated in Drupal 8 core. But above all, the biggest change in Drupal 8 is without doubt the integration of the Symfony framework, effectively shifting from a procedural-based to an object-oriented based framework, opening up the framework for a much larger audience, specifically for developers accustomed to the Symfony framework.
A few years ago we decided to standardise internal development, it was time to check out Drupal 8 and see if it was usable for production-ready websites. What better way to test a platform than using it for an actual project, with real-world requirements?
We started out with testing Drupal 8 in its early stages, even before the first beta release. From experience, we know that it is generally a bad idea to start with an actual project on non-beta versions, because usually the developers have not started on an upgrade path, meaning structural changes in the core may lead to loss of data and structure during upgrade, or worse yet, a non-functional platform. Nonetheless, we were determined to try it out on an actual project, otherwise we were sure we would miss out on finding bugs, missing features and other weaknesses.
We started out as we always do: setting up a basic site structure. We set up a few content types, changed their display modes, added a couple of views, set up a navigational structure and add some dummy content (we were still far away from having actual content). Then we started working on setting up a theme as we normally would: develop a base theme, based on a customised version of Twitter Bootstrap, and extend the base theme with a sub-theme for developing the actual layout for the site.
Things were smooth, we recognised a lot of best-practices from Drupal 7, and were very please to see they made it into core. Translations were easily set up, and were very glad that content translation is no longer an actual option, and entity (or field) translation was the default. Creating views
Immediately after we started exploring the capabilities of D8, we recognised a lot of best-practices from D7 made it to the core, and we were very happy to find out we followed these guidelines already, because this meant the switch to D8 was actually not too big for us.
Probably the biggest step for D8 was to incorporate the Symfony framework. Even though this meant basically a complete rewrite for a large part of Drupal, it was a very sensible step. Not only does it open up both D8 and Symfony to a whole new set of developers, but it also means the Drupal community could focus more on improving the CMS part, and not having to deal with maintaining the basic underlying structure. This also means a shift from a procedural system to a more object-oriented system. At first glance, the code is really well-structured, but it did scare us a bit: does this mean we will have to completely re-learn what we have learnt the past few years?
Plugins, Services and Hooks
Plugins and Services are basically the OO-equivalent of the old Hooks-based system, and it seems that even though a lot of Hooks are still present, they are slowly being phased out and meant to be replaced by either Plugins or Services.
Examples of Plugins are for instance modules providing custom types of blocks that have different properties than regular blocks, or plugins that provide new types of fields, such as a color or address field. These Plugins are provided by inheriting the default classes, such as BlockBase or FieldItemBase, and implementing the necessary methods.
Examples of Services are for instance a Cache services which reads from and writes to caching backend, such as Memcached, Redis, MongoDB or even the filesystem. Services are interchangeable, and provided by implementing several interfaces, in this case as the CacheBackendInterface.
Theming with Twig
Drupal 7 by default used PHPTemplate for its templating engine. Its setup was very flexible, and allowed other backends, but common practice means using PHPTemplate. For developers, it was a very easy system to use, since it used the same language as the core and modules use. Its immediate drawback therefor was that it allows developers to use inline PHP code, and therefor business login inside the templating engine. We have seen beginner developers do SQL queries from within the templates.
For Drupal 8, the Twig templating engine was chosen, mainly due to it being the engine of choice for Symfony. Twig itself is very simple, and requires very little time to get used to. The main difference between Drupal 7 and 8 is that in Drupal 8, everything has become a Twig template. In Drupal 7 you still had the choice of using either a template file, or a theme function, which directly outputs an HTML string. This sometimes made it difficult to determine where the output came from. In Drupal 8, basically everything is output using a Twig template, even small things like input fields or links. This enables much easier to develop templates, and allows for another cacheable layer, since Twig compiles to PHP code.
Contrib modules status
With most of our websites and platforms, the core functionality was simply not enough, so our install profile contains quite a few contrib modules by default. Some of these have been integrated into core, while others rely on the contributions of others.
We are a big fan of CSS preprocessors, such as SASS and LESS. We have been working with a custom theme, based on a customised version of Twitter Bootstrap. Twitter Bootstrap’s source is based on LESS, so it provided easy integration and customisation. Drupal 8, however, does not have a fully functioning LESS module, and although there were speculations of some kind of CSS preprocessor making it to Drupal 8 core, it never made the release, so we had to look for other options. Luckily, Grunt has very good LESS support with grunt-contrib-less, so we created a small task by using grunt-contrib-watch. Every time we saved our LESS file, the watch-task picks it up and re-compiles our CSS file for us. While this does not provide optimal Drupal integration (it generates one single CSS file for every page, so no option to generate separate LESS files per page), it did not change our basic workflow.
Display Suite (DS) is one of those modules that was always part of our toolset. It provides a nice way to determine the output of the HTML, from within the CMS, without having to resort to finding out where the HTML comes from in Drupal (was it a template file, or a render array, or a theme function?).
DS was at a beta phase when we started developing, but was pretty much usable, except for a few small things. However, a big problem with DS is that you have to configure everything from Drupal, there was no right way to things with code, meaning it was hard to version-control your adjustments, without having to resort to Drupal Features. With Drupal 8 now having everything coming from a Twig template file, it is rather easy to determine the output of every element from code, providing perfect versioning. This might mean we will no longer feel the need to use Display Suite.
Whenever we wanted to structure our output, we used Fieldgroup to provide us with wrapper elements to separate the fields of an entity. It however has the same issues regarding versioning as DS has, and although the Features integration is very good, we never really liked this workflow. Again, with Twig we felt it was no longer really necessary, as we could do everything we wanted using Twig templates.
Paragraphs and Panels
Both Paragraphs and Panels made up a big part of our workflow, depending on the size and complexity of the project. Both modules were supported from nearly the beginning, and were surprisingly stable. For building our current site, we only used Paragraphs, but testing out Panels gave very good results as well.
One of the bigger drawbacks of previous Drupal versions was the mix of data (content) and configuration (setup) within the database. A simple change in your development environment (for instance, the output of a field) was written to the database (as serialised PHP struct, no less). Preferably, such changes would be in code, so that it can be version-controlled and easily moved from environment to environment. The solution for this was using the Drupal Features module. Features basically integrated with Drupal by tracking changes in configuration settings (and data, if you would like to), and writing these out to PHP code, packages into a module. This works fine, but did mean that a lot of extra modules were required, and it also meant that modules need a way to let Features know their changes. This means integrating a lot of hooks that the Features module provides, and meant a lot of work for the developers.
Drupal 8 comes with an integrated configuration system, called, surprisingly, the Configuration System. Since it is in core, and is intended to be used as the default way for configuring a module’s settings, support was now site-wide. Configuration System is highly configurable, defaulting to writing to database, it integrates with a lot of backends, such as file-based storage (for versioning), MongoDB, Redis, etc. It even comes with default tools like Configuration Synchronizer.
This involved a lot of prototyping, reading up on tutorials and documentation, and getting used to the new way of developing modules.
All the standards we applied in Drupal 7 have become Drupal 8 standards now. Most of the contrib modules are still somewhat unstable & still in development. So before migrating our clients' websites we need to first be sure the modules used are stable so we can guarantee stability.
The code has changed substantially, so there's a lot we need to learn, but the structure is a lot better. Migrating from Drupal 7 to Drupal 8, seems quite bigger than migrating from Drupal 6 to Drupal 7.
All in all we are very excited and we will keep you posted on new developments.